2

I'm newcomer to C and I am stuck. I want to write simple program, which will take input from keyboard and output it if it isn't an 'exit' word. I've tried few different approaches and none of them works. Almost in all cases I get infinite output of the first input.

Here is one of my approaches:

#include <stdio.h>

int main() {
    char word[80];

    while (1) {
        puts("Enter a string: ");
        scanf("%79[^\n]", word);

        if (word == "exit")
            break;

        printf("You have typed %s", word);
    }

    return 0;
}

I thought after it finish every loop it should give me prompt again, but it doesn't. What I am doing wrong.

Please if you know give me some advice.

Thanks in advance. Really, guys I will be so happy if you help me to understand what I am doing wrong.

Oh, by the way I've noticed that when I typed some word and press 'Enter', the result string also include Enter at the end. How can I get rid of this ?

3
  • @Jens the loop is also broken, read the first paragraph of the post Commented Sep 16, 2014 at 20:28
  • 1
    You shouldn't modify the question to mean something entirely different! You can simply start multiple questions or amend it in a way that it's clear what you asked originally and what's new. Commented Sep 16, 2014 at 20:31
  • The current title is perfect. Commented Sep 16, 2014 at 20:32

5 Answers 5

3
  1. Improper string compare - use strcmp().

    if (word == "exit") simply compares 2 address: the address of the first char in word and the address of the first char in string literal "exit". Code needs to compare the content beginning at those addresses: strcmp() does that.

  2. Left-over '\n' from the previous line's Enter. Add a space to scanf() format to consume optional leading white-space. Also check scanf() results.

    scanf() specifiers like "%d", "%u" and "%f" by themselves consume optional leading white-space. 3 exceptions: "%c", "%n" and "%[".

  3. Add '\n' at end of printf() format. @ Matt McNabb

    #include <stdio.h>
    int main() {
        char word[80];
        while (1) {
            puts("Enter a string: ");
            //         v space added here
            if (scanf(" %79[^\n]", word) != 1) 
                break;  // Nothing saved into word or EOF or I/O Error
            if (strcmp(word, "exit") == 0)
                break;
            printf("You have typed %s\n", word);
        }
        return 0;
    }
    

Nice that OP used a proper width limited value of 79 in scanf()

Sign up to request clarification or add additional context in comments.

8 Comments

@OP: In the end, better to use fgets() to get user input and avoid scanf() altogether.
As far as I know all difference between scanf() and fgets() when applying to string is mandatory upper limit which you have to set in fgets(). Any other difference which I am not aware? I just wonder what the benefits of using fgets() instead of scanf() ?
so [^\n] at the end of " %79[^\n]" means do not include 'Enter' ? Is this correct to say like this ?
@Nikita Luparev Yes, "%[^\n]" means to scan in any char that is not a '\n' (AKA the Enter key). Note: curiously this includes '\0', but that occurs rarely in user input. Yes, correct to say. More: The 79 is the width to scan, just like in other specifiers "%5d" - scan up to 5 char.
@Nikita Luparev Benefits of fgets(word, sizeof word, stdin) v scanf(" %79[^\n]", word): 1) Error handling. fgets() gets a "line" of char, everything up to and including a potential trailing '\n'. That's it. 4 concerns: IO error, EOF, buffer full, embedded '\0'. 2) no left-over '\n' remaining in the IO buffer.
|
2

Oh, by the way I've noticed that when I typed some word and press 'Enter', the result string also include Enter at the end. How can I get rid of this ?

This is because you don't output a newline after printf("You have typed %s", word);. The next statement executed is puts("Enter a string: "); . So you will see You have typed helloEnter a string:. To fix this, change to printf("You have typed %s\n", word);

As others have mentioned, use strcmp to compare strings in C.

Finally, the scanf format string "%79[^\n]" does not match a newline. So the input stream still contains a newline. Next time you reach this statement the newline is still in the stream , and it still doesn't match because you specifically excluded newlines.

You will need to discard that newline (and any other input on the line) before getting the next line. One way to do that is to change the input to scanf("%79[^\n]%*[^\n]", word); getchar(); That means:

  • Read up to 79 non-newlines
  • Read all the non-newline things , and don't store them
  • Read a character (which must be a newline now) and don't store it

Finally it would be a good idea to check the return value of scanf so that if there is an error then you can exit your program instead of going into an infinite loop.

1 Comment

Thank you Matt, you answer is very precise and exhaustive at the same time. Let me say in my own words, [^\n] means exclude any carriage to next line symbol (Enter) ? Am I right ? "%79[^\n] also means read 79 characters up to carriage return symbol (Enter) ?
2

The specifier [^\n] will abort scanf if the next character is a newline (\n), without reading the newline. Because of that, the scanf calls after the first one won't read any input.

If you want to read single words, use the %79s specifier and the following code to remove the \n at the end of your string:

if(word[strlen(word)]=='\n')
    word[strlen(word)]='\0';

If you want to read whole lines, you can remove the newline from the input buffer this way:

char line[80];
int i;
while(1)
{
    puts("Enter a string:");
    i=-1;
    scanf("%79[^\n]%n",line,&i);
    //%n returns the number of characters read so far by the scanf call

    //if scanf encounters a newline, it will abort and won't modify i
    if(i==-1)
        getchar(); //removes the newline from the input buffer

    if(strcmp(line,"exit")==0)
        break;

    printf("You have typed %s\n",line);
}
return 0;

5 Comments

Certain code should not use the 's' for in "%79[^\n]s%n".
you said - "The specifier [^\n] will abort scanf if the next character is a newline" is excatly the answer I looked for. Thank you. Next you wrote - " Because of that, the scanf calls after the first one won't read any input.", mmm.. I don't get it. Please, could you explaing one more time.
scanf("%79[^\n]s%n",line,&i) - 'i' at the acts as a counter, stores how much symbols are readed ?
@Nikita Luparev "%n" does indeed store how many char are read - if scanning get to it. The 's' in "%79[^\n]s%n" would almost always prevent that.
@NikitaLuparev You type hello world\n. The first scanf reads hello world and aborts because of the \n, without removing it from the input buffer. The next scanf calls don't read anything, because the first character they meet is the \n. About the %n, if the scanf doens't abort midway , the i will store how many characters were read until the %n was reached. @chux Why does the s prevent that?
1

It is better to clear (to have a reproducible content) with memset(3) the memory buffer before reading it, and you should use strcmp(3) to compare strings. Also, consider using fflush(3) before input (even if it is not actually necessary in your case), don't forget to test result of scanf(3), also most printf(3) format control strings should end with a \n -for end-of-line with flushing- so:

#include <stdio.h>
int main() {
  char word[80];
  while(1) {
     puts("Enter a string: ");
     memset (word, 0, sizeof(word)); // not strictly necessary
     fflush(stdout); // not strictly necessary
     if (scanf("%79[^\n]", word)<=0)  exit(EXIT_FAILURE);
     if (!strcmp(word,"exit"))
        break;
     printf("You have typed %s\n", word);
  };
  return 0; 
}

I would suggest reading a whole line with fgets(3) and getting rid of its ending newline (using strchr(3)). Also read about getline(3)

Don't forget to compile with all warnings and debug info (e.g. gcc -Wall -g) and learn how to use the debugger (e.g. gdb)

2 Comments

The memset is not necessary either, since your code either writes a null-terminated string via scanf, or exits.
Suspect that the keyboard input of '\n' is still not consumed anywhere.
0

Your first problem is that you can't compare a string with '=='. So:

if (word == "exit")

should be

if ( strncmp( word, "exit", 4 ) == 0 )

(You could also use strncmp( word, "exit", strlen(word) ) if you know that word is zero-terminated and safe from bad values. There's a few other options also.)

Your second problem is that scanf() is not consuming the input, probably because it's not matching what you've told it to expect. Here is a good explanation of how to do what you want to do:

http://home.datacomm.ch/t_wolf/tw/c/getting_input.html

7 Comments

Why strncmp instead of strcmp?
It's good practice to always use strncmp instead of strcmp if possible in order to avoid buffer overflow issues. You should always track array/string lengths with the array/string and check the lengths when using the array/string.
No length is tracked in this code snippet. It's hard-coded. (And wrong, btw, for example "exitt" would also be considered equal.) String literals are always 0-terminated, if at all, you should use the length of word. Care is already taken to have word 0-terminated; if it wasn't, there were troubles printing it etc anyway. And if you do keep track of the lengths, you can use the mem* functions.
@mafso general agree other than should use the strlen(word) + 1 to prevent "exitt" from matching.
@JonS It's good to point out such things, especially for beginners. But here, looking at the "%79[^\n]", OP's already aware of buffer overflows and space for 0-terminators. The issue with "exitt" isn't that important, I just wanted to point that out (and I strongly believe, this is what OP expected == to do, as this is the behaviour in some other languages). The important thing is: If word is 0-terminated, strncmp doesn't buy you anything, and if it isn't, it will still be out-of-bounds if word is shorter than 4, or, for the second example, it will also break with the strlen call.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.