Quite a simple program:
int main (void)
{
int i = 0, length=0;
char password[] = SECRET;
char guess[10];
for (i=0; i<3; i++){
printf( "Enter the password: " );
fgets (guess, 10, stdin );
length=strlen(guess);
guess[length]='\0';
if(strcmp( guess, password ) == 0 ){
printf("\aYou got it right!\n" );
return 0;
}
else printf("You wrote %s Incorrect guess\n\n", guess);
}
puts("Sorry, you're all out of guesses");
return 0;
}
But it doesn't work.
Even in the bizarre situation where I can get the program to say:
"You guessed 'black'. Sorry, the password was 'black'"
Thought there might be some issue with some hidden character, blank space, garbage information or whatever throwing off the string comparison, but I can't seem to find what it is!
If you learn to use the debugger, you would probably see that the string returned by fgets() includes the newline character, which doesn't match the string you compare to.
And what is the point of this code:
length=strlen(guess);
guess[length]='/0';
First of all, I can only assume you meant \0 instead of /0. And second, strlen() works by locating the null terminator. So what's the point of finding the terminator and then writing a terminator at the same location?
instead of making:
length = strlen(guess);
guess[length]='\0';
you should make this:
char *s = strchr(guess, '\n');
if (s) {
// new line is found
*s = 0;
} else {
// user has exceeded max chars
guess[sizeof(guess) - 1] = 0;
}
I advice you to use strncmp instead of strcmp to avoid buffer overflow attacks.
Related
So I'm writing a program that will loop forever, accepting string inputs until the user just presses enter with no string (along the way, I'm tracking the longest/shortest strings entered). I have this loop:
char stringIn[1000] = {'\0'};
while(1) {
scanf("%[^\n]s", stringIn);
if(stringIn[0] == '\0') {
break;
}
if(strlen(stringIn) > strlen(longString)) {
longString == stringIn;
} else if (strlen(stringIn) < strlen(shortString)) {
shortString == stringIn;
}
i++;
}
Currently this just loops forever. I'm still really new to C, but to me this looks like it should've worked.
Points to note:
You probably mistook the == operator for =, which is assignment. Even so, it wouldn't work because here it would only copy addresses of buffers (which get overwritten) (actually in my code it would throw a compile time errors). For copying strings you wanna use strcpy.
scanf is pretty vulnerable to buffer overflows and leaves the delimiter in the buffer. fgets is a much better choice for reading lines as it takes a buffer length as argument (check this out).
scanf fills a number of items in it's list until characters matching the format string are read. If no chars match, then it doesn't fill stringIn, and hence doesn't append a '\0' at the end, and that's why your code never goes to break;. Instead we can use the return value, which is the number of items of the list that it fills (see here).
Anyway, here is code that does what you want:
int main() {
char stringIn[1000] = "";
char longString[2000] = "", shortString[2000] = "";
int read, firstFlag = 0;
while(1) {
read = scanf("%[^\n]", stringIn);
if (read == 0) {
break;
}
// to consume the '\n' left by scanf in the buffer
getchar();
if (!firstFlag || strlen(stringIn) > strlen(longString)) {
strcpy(longString, stringIn);
}
if (!firstFlag || strlen(stringIn) < strlen(shortString)) {
strcpy(shortString, stringIn);
}
firstFlag = 1;
}
printf("%s, %s\n", longString, shortString);
return 0;
}
UPDATE: Edited according to Jonathan Leffler's comment above, correcting the use of the scanset.
(Sorry for my bad english !)
I wrote a program that asks you to type a password no longer than a certain number, eight characters in this case. The characters that pass the limit will be cut out from the array:
#include <stdio.h>
#define MAXCHAR 8
main()
{
char password[MAXCHAR];
int i;
char c;
printf("Insert password: MAX 8 CHARS!\n\n");
for(i = 0; i <= MAXCHAR; i++){
c = getchar();
if(i == MAXCHAR){
break;
}
else{
password[i] = c;
}
}
printf("%s\n", password);
}
So the program works BUT there is a "strange" problem. If the limit IS EIGHT and I type a password longer than eight characters
(Example: P455w0rds98)
the output will be like this:
P455w0rd☺
So it puts a smiley at the end and I don't know why. It happens only if a the limit is established at eight.
You must specify the length to print or terminate the string. Otherwise, you will invoke undefined behavior. Try this, in which the latter method is implemented.
#include <stdio.h>
#define MAXCHAR 8
int main(void)
{
char password[MAXCHAR + 1]; /* allocate one more element for terminating null-character */
int i;
char c;
printf("Insert password: MAX 8 CHARS!\n\n");
for(i = 0; i <= MAXCHAR; i++){
c = getchar();
if(i == MAXCHAR){
break;
}
else{
password[i] = c;
}
}
password[MAXCHAR] = '\0'; /* terminate the string */
printf("%s\n", password);
}
Some people say that the if(i == MAXCHAR){ break; } part doesn't look good, so here is another code example:
#include <stdio.h>
#define MAXCHAR 8
int main(void)
{
char password[MAXCHAR + 1]; /* allocate one more element for terminating null-character */
int i;
printf("Insert password: MAX 8 CHARS!\n\n");
/* read exactly 8 characters. To improve, breaking on seeing newline or EOF may be good */
for(i = 0; i < MAXCHAR; i++){
password[i] = getchar();
}
password[MAXCHAR] = '\0'; /* terminate the string */
getchar(); /* to match number of call of getchar() to the original: maybe for consuming newline character after 8-digit password */
printf("%s\n", password);
}
All C-style strings have a terminal \0 character (value 0). This is unique from any other character value, so it can be used to signal the end of the string. The smiley face you observe is just a part of some neighboring memory block that happens to have a null character after the first byte (hence there being only one extra character). The printf function reads bytes from the string given to it, until it sees the \0. To solve your problem, you can either write
password[MAXCHAR] = '\0';
(You will need to reserve one additional byte in your array, for the \0).
Or you can zero-out your array from the get-go:
char password[MAXCHAR + 1] = { };
Or using memset:
memset(password, '\0', sizeof password);
Apart from the answer you already received from MikeCAT, an alternate approach would be to make use of fgets() to read the user input.
In that case , you don't need to keep a count on each character input, you can specify the max size and get done with it. Something like
fgets(password, MAXCHAR, stdin);
can get the job done for you, minus the looping and assignment for each element.
One thing to remember, however, for shorter inputs than the given length, fgets() reads and stores the trailing newline also, you may need to get rid of that manually. Read the linked man page for more ideas.
That said, main() is a very bad and almost non-standard for hosted environments. You should use int main(void), at least to conform to the standards.
I'm a beginner programmer trying to learn C. Currently I'm taking a class and had a project assigned which I managed to finish pretty quickly, at least the main part of it. I had some trouble coding around the main() if functions though, because I started using some new functions (that is, fgets and strncmp). Now, my code works in my compiler, but not in any of the online compilers. So I'm wondering if I did something wrong with it, or if there is any way I can improve it.
Any help or contribution is appreciated, thanks!
Below is the code, the encrypt and decrypt functions are the first two functions before the main, where I believe most of the messy shortcut-code might be.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * Encrypt(char sentence[])
{
int primes[12] = {1,2,3,5,7,11,13,17,19,23,29,31};
int x = 0;
int counter = 0;
int ispositive = 1;
while(sentence[x] != 0)
{
if (counter == 0)
{
ispositive = 1;
}
else if(counter == 11)
{
ispositive = 0;
}
if (ispositive == 1)
{
sentence[x] = sentence[x] + primes[counter];
counter++;
}
else if (ispositive == 0)
{
sentence[x] = sentence[x] + primes[counter];
counter--;
}
x++;
}
return sentence;
}
char * Decrypt(char sentence[])
{
int primes[12] = {1,2,3,5,7,11,13,17,19,23,29,31};
int x = 0;
int counter = 0;
int ispositive = 1;
while(sentence[x] != 0)
{
if (counter == 0)
{
ispositive = 1;
}
else if(counter == 11)
{
ispositive = 0;
}
if (ispositive == 1)
{
sentence[x] = sentence[x] - primes[counter];
counter++;
}
else if (ispositive == 0)
{
sentence[x] = sentence[x] - primes[counter];
counter--;
}
x++;
}
return sentence;
}
int main()
{
char message[100];
char input[7];
char *p;
int c;
int condition = 1;
while(condition == 1)
{
printf("Would you like to Encrypt or Decrypt a message? (Type TurnOff to end the program) \n \n");
fgets(input,7, stdin);
fflush(stdin);
if (!strncmp(input,"Encrypt",strlen(input)))
{
printf("\n \n Enter the message you want to Encrypt below: \n \n");
fgets(message, 100, stdin);
Encrypt(message);
printf("\n Your encrypted message is: ");
printf("%s", message);
fflush(stdin);
printf("\n \n");
}
else if (!strncmp(input,"Decrypt",strlen(input)))
{
printf("\n \n Enter the message you want to Decrypt below: \n \n");
fgets(message, 100, stdin);
Decrypt(message);
printf("\n Your Decrypted message is: ");
printf("%s", message);
fflush(stdin);
printf("\n \n");
}
else if (!strncmp(input,"TurnOff",strlen(input)))
{
printf("\n \n Thank you for using the program! \n \n");
condition = 0;
}
else
{
printf("That's not a valid input \n \n");
}
}
}
After the printf you doing fflush(stdin) instead of you have to do fflush(stdout). Because you are printing the output. The output is printed in stdout. So, you have to flush the stdout buffer not stdin buffer.
You can use the strcmp instead of strncmp. Because in here you are comparing the hole character in the input array. So, the strcmp is enough.
strcmp(input, "Encrypt").
The strcmp or strncmp function get the input in array upto a null or the size of the string you are declared.
The size for the input array is too few.
lets take the input is like below.
Encrypt\n
sureshkumar\n
In here you first fgets in main function reads the upto "Encrypt" it does not skip the '\n'.
The '\n' is readed form another fgets. So, it does not get the encrypt message "sureshkumar".
So, you have to modify you code. You will increase the size for the input array.
And check the condition like below.
if(strcmp(input, "Encrypt\n") == 0)
{
/*
You will do what you want
*/
}
You can use the above way or you can read the input and overwrite the '\n' to '\0' in the input array and compare as it is you before done. But you have to use the strcmp. Because the array size is incremented.
This is the right way for using the fgets. Use of fgets is to read upto new line.
You have to use the null character for the character array. Because this is necessary for the character arrays.
Your initiative towards using strcmp() and fgets() is good, though, it requires following understanding:
1. fgets() writes atmost size-1 characters into buffer and then terminates with '\0'. In your case,
fgets(input,7, stdin);
You gave input "Encrypt"/"Decrypt"/"TurnOff"
but
'input' buffer got data as "Encryp"/"Decryp"/"TurnOf"
because of size=7 (only (7-1)=6 characters being read, last position reserved for '\0' character by fgets()).
Your strncmp() calls will work correctly with your current code, since for strncmp(), length to compare
n = strlen(input) = 6;
6 characters are matching fine in all three cases of "Encrypt"/"Decrypt"/"TurnOff".
Summary is that your current code will work fine, But your actual intention is violated. You actually wanted to read and compare full length of option string.
EDIT DONE : Modifications suggested:
#define SIZE 9 <-- EDIT : Change done here, instead of 7, size = 9 is used
to allow reading '\n' so that it does not affect
fgets() read in successive iteration
char input[SIZE];
fgets(input, SIZE, stdin); // read str is e.g. "Encrypt\n"
input[SIZE-2] = '\0'; // To replace '\n' with '\0'
Similarly, you need to be careful when reading into 'message' array using fgets().
This program essentially asks for a secret string, then asks a user to repeatedly guess single chars of that string until he guesses it all. It works however every second time the while loop is run it skips user input for the guessed char. How do I fix this?
int main(){
char guess;
char test2 [50];
char * s = test2;
char output [50];
char * t = output;
printf("Enter the secret string:\n");
fgets(test2, 50, stdin);
for (int i=0;i<49;i++){ //fills ouput with _ spaces
*(output +i)='_';
while(strcmp(s,t) != 0){
printf("Enter a guess:");
scanf("%c",&guess);
printf("You entered: %c\n", guess);
showGuess(guess,s, t ); // makes a string "output" with guesses in it
printf("%s\n",t);
}
printf("Well Done!");
}
For a quick and dirty solution try
// the space in the format string consumes optional spaces, tabs, enters
if (scanf(" %c", &guess) != 1) /* error */;
For a better solution redo your code to use fgets() and then parse the input.
As pointed out in some other answers and comments, you need to "consume" the "newline character" in the input.
The reason for that is that the input from your keyboard to the program is buffered by your shell, and so, the program won't see anything until you actually tell your shell to "pass the content of its buffer to the program". At this point, the program will be able to read the data contained in the previous buffer, e.g. your input, followed by one the character(s) used to validate your input in the shell: the newline. If you don't "consume" the newline before you do another scanf, that second scanf will read the newline character, resulting in the "skipped scanf" you've witnessed. To consume the extra character(s) from the input, the best way is to read them and discard what you read (what the code below does, notice the
while(getc(stdin) != '\n');
line after your scanf. What this line does is: "while the character read from stdin is not '\n', do nothing and loop.").
As an alternative, you could tell your shell to not buffer the input, via the termios(3) functions, or you could use either of the curses/ncurses libraries for the I/O.
So here is what you want:
int main(){
char guess;
char test2 [50];
char * s = test2; // 3. Useless
char output [50];
char * t = output; // 3. Useless
int i; // 8. i shall be declared here.
printf("Enter the secret string:\n");
fgets(test2, 50, stdin);
for (i=0;i<50;i++) if (test2[i] == '\n') test2[i] = '\0'; // 4. Remove the newline char and terminate the string where the newline char is.
for (int i=0;i<49;i++){ // 5. You should use memset here; 8. You should not declare 'i' here.
*(output +i)='_';
} // 1. Either you close the block here, or you don't open one for just one line.
output[49] = '\0'; // 6. You need to terminate your output string.
while(strcmp(s,t) != 0){ // 7. That will never work in the current state.
printf("Enter a guess:");
scanf("%c",&guess);
while(getc(stdin) != '\n');
printf("You entered: %c\n", guess);
showGuess(guess,s, t );
printf("%s\n",t);
}
printf("Well Done!");
return 0; // 2. int main requires that.
}
Other comments on your code:
You opened a block after your for loop and never closed it. That might be causing problems.
You declared your main as a function returning an integer... So you should at least return 0; at the end.
You seem to have understood that char * t = output; copies output's value and uses t as a name for the new copy. This is wrong. You are indeed copying something, but you only copy the address (a.k.a reference) of output in t. As a result, output and t refer to the same data, and if you modify output, t will get modified; and vice versa. Otherwise said, those t and s variables are useless in the current state.
You also need to remove the newline character from your input in the test2 buffer. I have added a line after the fgets for that.
Instead of setting all the bytes of an array "by hand", please consider using the memset function instead.
You need to actually terminate the output string after you "fill" it, so you should allocate a '\0' in last position.
You will never be able to compare the test2 string with the output one, since the output one is filled with underscores, when your test2 is NULL terminated after its meaningful content.
While variables at the loop scope are valid according to C99 and C11, they are not standard in ANSI C; and it is usually better to not declare any variable in a loop.
Also, "_ spaces" are called "underscores" ;)
Here is a code that does what you want:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN 50
int main()
{
char phrase[LEN];
char guessed[LEN];
char guess;
int i, tries = 0;
puts("Please enter the secret string:");
if(fgets(phrase, LEN, stdin) == NULL)
return 1;
for(i = 0; i < LEN && phrase[i] != '\n'; i++); // Detect the end of input data.
for(; i < LEN; i++) // For the rest of the input data,
phrase[i] = '_'; // fill with underscores (so it can be compared with 'guessed' in the while loop).
phrase[LEN - 1] = '\0'; // NULL terminate 'phrase'
memset(guessed, '_', LEN); // Fill 'guessed' with underscores.
guessed[LEN - 1] = '\0'; // NULL terminate 'guessed'
while(strcmp(phrase, guessed) != 0) // While 'phrase' and 'guessed' differ
{
puts("Enter a guess (one character only):");
if(scanf("%c", &guess) != 1)
{
puts("Error while parsing stdin.");
continue;
}
if(guess == '\n')
{
puts("Invalid input.");
continue;
}
while(getc(stdin) != '\n'); // "Eat" the extra remaining characters in the input.
printf("You entered: %c\n", guess);
for(i = 0; i < LEN; i++) // For the total size,
if(phrase[i] == guess) // if guess is found in 'phrase'
guessed[i] = guess; // set the same letters in 'guessed'
printf("Guessed so far: %s\n", guessed);
tries++;
}
printf("Well played! (%d tries)\n", tries);
return 0;
}
Feel free to ask questions in the comments, if you are not getting something. :)
Newline character entered in the previous iteration is being read by scanf. You can take in the '\n' by using the getc() as follows:
scanf("%c",&guess);
getc(stdin);
..
This changed worked for me. Though the right explanation and c leaner code is the one given by #7heo.tk
Change
scanf("%c",&guess);
with
scanf(" %c",&guess);
It should ignore '\n'.
I have a delete function on array of structures books. I'm passing it an array of records, author of book and name of book and size of the list.
Now here given that list[0].author and list[5].author and author all are equal to "Dan Brown" (same string)
void delete(struct books *list,char author[],char name[],int n)
{
int i,a;
a=strcmp(list[0].author,list[5].author);
printf("%d\n",a); // prints 0
a=strcmp(list[0].author,author);
printf("%d\n",a); // prints other than 0
}
Why is it happening? What's wrong here?
From the documentation of fgets:
Reading stops when a newline character is found, at end-of-file or error. The newline, if any, is retained.
This means that fgets will not remove the final '\n' from the end of the read string. Thus, your strings are:
"Dan Brown"
"Dan Brown"
"Dan Brown\n"
They're not equal.
This is a very common issue when using fgets. That's why I usually prefer scanf, like this:
char buffer[BUF_LEN];
char format[16];
int scanf_result;
sprintf(format, "%%%u[^\n]", BUF_LEN);
//....
do
{
//TODO: Ask for input
scanf_result = scanf(format, buffer);
switch (scanf_result)
{
case -1: //TODO: Print error message and exit
case 0: //TODO: Print error mesage and break
}
//Discard remainings of buffered input line
while (getchar() != '\n') {;}
} while (1); //Ugly, but plain
Otherwise, you can use fgets with something like this:
int buf_len;
//TODO: Ask for input
while (fgets(buffer, BUF_LEN, stdin) == NULL)
{
//TODO: Check and handle error
}
buf_len = strlen(buffer);
//Remove trailing '\n', if present
if (buffer[buf_len - 1] == '\n')
{
buffer[--buf_len] = '\0';
}
Even though it's easier, I don't like this second method, because strlen scans the string another time to determine its length. In most cases, this is not a performance issue, I avoid it because I have my own mental issues.
You should verify your inputs. Sometimes by more than one method is necessary. Here, I am using strlen(), and strstr(), because if the length is ==, and a substring exists, then the strings ARE equal. So, try something like this to verify the input strings are what you thing they are before making a conclusion:
Note: the enum is of course not necessary, but included here to add clarity to example of output.
enum {
SAME, //0
NOT_SAME //1
}
void delete(struct books *list,char author[],char name[],int n)
{
int i,a, len1, len2;
A = NOT_SAME;
len1 = strlen(list[0].author);
len2 = (list[5].author);
if(strstr(list[0].author,list[5].author) && (len1==len2)) a = SAME;
printf("%d\n",a);
a = NOT_SAME;
len1 = strlen(list[0].author);
len2 = (author);
if(strstr(list[0].author,author) && (len1==len2)) a = SAME;
printf("%d\n",a);
}
check second strings by printing character by character.
Especially author string.
for(i=0; i < strlen(list[0].author);i++)
{
if(list[0].author[i]!=author[i])
{
printf("this is position is not matching\n",i+1);
//try to print characters and also print ascii characters.
break;
}
}
//or simply try to use strncpy()