Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why won't this scanf format-string work? "%[^\n]\n"

Tags:

c

input

scanf

I've seen a few examples where people give scanf a "%[^\n]\n" format string to read a whole line of user input. If my understanding is correct, this will read every character until a newline character is reached, and then the newline is consumed by scanf (and not included in the resulting input).

But I can't get this to work on my machine. A simple example I've tried:

#include <stdio.h>

int main(void)
{
    char input[64];

    printf("Enter some input: ");
    scanf("%[^\n]\n", input);

    printf("You entered %s\n", input);
}

When I run this, I'm prompted for input, I type some characters, I hit Enter, and the cursor goes to the beginning of the next line but the scanf call doesn't finish.

scanf not finished

I can hit Enter as many times as I like, and it will never finish.

scanf still not finished

The only ways I've found to conclude the scanf call are:

  • enter \n as the first (and only) character at the prompt
  • enter Ctrl-d as the first (and only) character at the prompt
  • enter some input, one or more \n, zero or more other characters, and enter Ctrl-d

I don't know if this is machine dependent, but I'm very curious to know what's going on. I'm on OS X, if that's relevant.

like image 960
ivan Avatar asked Feb 07 '16 16:02

ivan


3 Answers

According to the documentation for scanf (emphasis mine):

The format string consists of whitespace characters (any single whitespace character in the format string consumes all available consecutive whitespace characters from the input), non-whitespace multibyte characters except % (each such character in the format string consumes exactly one identical character from the input) and conversion specifications.

Thus, your format string %[^\n]\n will first read (and store) an arbitrary number of non-whitespace characters from the input (because of the %[^\n] part) and then, because of the following newline, read (and discard) an arbitrary number of whitespace characters, such as spaces, tabs or newlines.

Thus, to make your scanf stop reading input, you either need to type at least one non-whitespace character after the newline, or else arrange for the input stream to end (e.g. by pressing Ctrl+D on Unix-ish systems).

Instead, to make your code work as you expect, just remove the last \n from the end of your format string (as already suggested by Umamahesh P).

Of course, this will leave the newline still in the input stream. To get rid of it (in case you want to read another line later), you can getc it off the stream, or just append %*c (which means "read one character and discard it") or even %*1[\n] (read one newline and discard it) to the end of your scanf format string.

Ps. Note that your code has a couple of other problems. For example, to avoid buffer overflow bugs, you really should use %63[^\n] instead of %[^\n] to limit the number of characters scanf will read into your buffer. (The limit needs to be one less than the size of your buffer, since scanf will always append a trailing null character.)

Also, the %[ format specifier always expects at least one matching character, and will fail if none is available. Thus, if you press enter immediately without typing anything, your scanf will fail (silently, since you don't check the return value) and will leave your input buffer filled with random garbage. To avoid this, you should a) check the return value of scanf, b) set input[0] = '\0' before calling scanf, or c) preferably both.

Finally, note that, if you just want to read input line by line, it's much easier to just use fgets. Yes, you'll need to strip the trailing newline character (if any) yourself if you don't want it, but that's still a lot easier and safer that trying to use scanf for a job it's not really meant for:

#include <stdio.h>
#include <string.h>

void chomp(char *string) {
    int len = strlen(string);
    if (len > 0 && string[len-1] == '\n') string[len-1] = '\0';
}

int main(void)
{
    char input[64];

    printf("Enter some input: ");
    fgets(input, sizeof(input), stdin);
    chomp(input);

    printf("You entered \"%s\".\n", input);
}
like image 83
Ilmari Karonen Avatar answered Sep 22 '22 01:09

Ilmari Karonen


Whitespace characters in format of scanf() has an special meaning:

Whitespace character: the function will read and ignore any whitespace characters encountered before the next non-whitespace character (whitespace characters include spaces, newline and tab characters -- see isspace). A single whitespace in the format string validates any quantity of whitespace characters extracted from the stream (including none).

Thus, "%[^\n]\n" is just equivalent to "%[^\n] ", telling scanf() to ignore all whitespace characters after %[^\n]. This is why all '\n's are ignored until a non-whitespace character is entered, which is happened in your case.

Reference: http://www.cplusplus.com/reference/cstdio/scanf/

like image 43
nalzok Avatar answered Sep 20 '22 01:09

nalzok


Remove the the 2nd new line character and the following is sufficient.

scanf("%[^\n]", input);

To answer the original one,

scanf("%[^\n]\n", input);

This should also work, provided you enter a non white space character after the input. Example:

Enter some input: lkfjdlfkjdlfjdlfjldj
t
You entered lkfjdlfkjdlfjdlfjldj
like image 36
Umamahesh P Avatar answered Sep 20 '22 01:09

Umamahesh P