4

My objective is to change the delimiter of scanf to "\n". I tried using scanf("%[^\n]s",sen); and works fine for single inputs. But when i put the same line inside a for loop for multiple sentences it gives me garbage values.

Does anyone know why?


Here's my code:

char sen[20];
for (i=0;i<2;i++)
{
    scanf("%[^\n]s",sen);
    printf("%s\n",sen);
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
firecast
  • 988
  • 2
  • 10
  • 20
  • works in the sense it reads a line from the terminal with `\n` as the delimiter and i forgot to put the % sign, edited it though. – firecast Oct 14 '12 at 19:27
  • I think you just need to remove the final `'s'` from the format string. The `%[^\n]` specifies to read characters up to the next `'\n'`, then the `'s'` wants a literal `'s'` in the input. – Daniel Fischer Oct 14 '12 at 19:31
  • tried it just with `%[^\n]` it still gives garbage values – firecast Oct 14 '12 at 19:35
  • 1
    @firecast You need to remove the newline from the input buffer. – Daniel Fischer Oct 14 '12 at 19:42
  • Just noticed that the compiler doesn't execute the `scanf` statement inside the `for` loop but it does go inside the loop. Used to taking inputs from a file so din't notice it earlier. – firecast Oct 14 '12 at 19:44
  • @firecast: Welcome to Stack Overflow. You should have posted an SSCCE (a [Short, Self-Contained, Correct (Compiling) Example](http://sscce.org/)), similar to the code in my answer, showing what you're doing. The compiler doesn't execute your C program; the O/S does. If your C program has a `scanf()` in a `for` loop, the compiler and linker will ensure that the `scanf()` function _is_ called inside the loop — unless there's some other condition that prevents it being called. Please read the [FAQ] soon. – Jonathan Leffler Oct 14 '12 at 19:48

5 Answers5

16

Consider this (C99) code:

#include <stdio.h>

int main(void)
{
    char buffer[256];

    while (scanf("%255[^\n]", buffer) == 1)
        printf("Found <<%s>>\n", buffer);
    int c;
    if ((c = getchar()) != EOF)
        printf("Failed on character %d (%c)\n", c, c);
    return(0);
}

When I run it and type in a string 'absolutely anything with   spaces TABTABtabs galore!', it gives me:

Found <<absolutely anything with   spaces       tabs galore!>>
Failed on character 10 (
)

ASCII (UTF-8) 1010 is newline, of course.

Does this help you understand your problem?


It works in this case (for a single line) but if I want to take multiple lines of input into an array of arrays then it fails. And I don't get how scanf returns a value in your code?

There are reasons why many (most?) experienced C programmers avoid scanf() and fscanf() like the plague; they're too hard to get to work correctly. I'd recommend this alternative, using sscanf(), which does not get the same execration that scanf() and fscanf() do.

#include <stdio.h>

int main(void)
{
    char line[256];
    char sen[256];

    while (fgets(line, sizeof(line), stdin) != 0)
    {
        if (sscanf(line, "%255[^\n]", sen) != 1)
            break;
        printf("Found <<%s>>\n", sen);
    }
    int c;
    if ((c = getchar()) != EOF)
        printf("Failed on character %d (%c)\n", c, c);
    return(0);
}

This reads the line of input (using fgets() which ensures no buffer overflow (pretend that the gets() function, if you've heard of it, melts your computer to a pool of metal and silicon), then uses sscanf() to process that line. This deals with newlines, which are the downfall of the original code.

char sen[20];
for (i=0;i<2;i++)
{
    scanf("%[^\n]s",sen);
    printf("%s\n",sen);
}

Problems:

  1. You do not check whether scanf() succeeded.
  2. You leave the newline in the buffer on the first iteration; the second iteration generates a return value of 0 because the first character to read is newline, which is the character excluded by the scan set.
  3. The gibberish you see is likely the first line of input, repeated. Indeed, if it were not for the bounded loop, it would not wait for you to type anything more; it would spit out the first line over and over again.

Return value from scanf()

The definition of scanf() (from ISO/IEC 9899:1999) is:

§7.19.6.4 The scanf function

Synopsis

 #include <stdio.h>
 int scanf(const char * restrict format, ...);

Description

2 The scanf function is equivalent to fscanf with the argument stdin interposed before the arguments to scanf.

Returns

3 The scanf function returns the value of the macro EOF if an input failure occurs before any conversion. Otherwise, the scanf function returns the number of input items assigned, which can be fewer than provided for, or even zero, in the event of an early matching failure.

Note that when the loop in my first program exits, it is because scanf() returned 0, not EOF.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • It works in this case(for single line) but if I want to take multiple lines of input into an array of arrays then it fails. And i din't get how `scanf` returns a value in your code? – firecast Oct 14 '12 at 19:59
  • 1
    the code provided by you works fine. But just out of curiosity i accidently changed my code from `scanf("%[^\n]s",sen);` to `scanf(" %[^\n]s",sen);` and it started working fine. Do you know why? – firecast Oct 14 '12 at 20:33
  • The space at the front of the format eats stray white space, including newlines (possibly several of them), before starting to process the scan-set conversion. It matters for the `%c` and scan-set conversions; all others AFAICR already skip leading white space anyway. The subtleties of the `scanf()` format strings are many; it is a very complex function to use correctly. You should take time to read the standard sometime (available from ANSI for $35 in PDF—well worth the investment if you're going to be a serious C programmer), or at the very least your system's man page. And reread it, often. – Jonathan Leffler Oct 14 '12 at 20:39
  • @JonathanLeffler : also, what would be the equivalent of`"%255[^\n]"`for getting an integer instead of a string *(`%d`)*? – user2284570 Mar 25 '15 at 02:59
  • @user2284570: I don't understand your earlier comment. Your later comment can be construed several ways. You could use `%d` or `%ld` or `%lld` or `"%" PRIdMAX` to get a possibly-signed integer. I don't think you can stop the user entering or the input accepting a sign (`+` or `-`). If you want to input a string of digits, then you can use `%255[0-9]` to input up to 255 digits followed by a null byte (so the variable needs to be `char buffer[256];` or bigger). – Jonathan Leffler Mar 25 '15 at 03:13
  • @JonathanLeffler : The first comment referred to [this question](http://stackoverflow.com/q/29213463/2284570). You didn’t understood the secound comment : I was trying this`char buffer[256]; while (scanf("%255[^\n]", buffer) == 1)`from your example, but in the aim of getting an integer so buffer would be declared as`int buffer;`instead of`char buffer[256]`. Also`[^\n]`is for matching until a newline but how I can match until ctrl+Z is pressed *(when`^Z`is printed on the screen)*? – user2284570 Mar 25 '15 at 03:25
  • @user2284570: I think you need to ask a question about this; I'm not sure I understand exactly what you're running into as problems. Note that the `scanf("%255[^\n]", buffer)` leaves newlines in the buffer; you can't use the same format to read the newline. If you're simply trying to slurp until EOF (control-Z on Windows, I assume, not on Unix where control-Z has a different meaning), you probably end up using ... well, there are both embarrassingly many and depressingly few functions. On POSIX, you'd use `getline()`; I expect there's an analogue on Windows, but I don't know its name. – Jonathan Leffler Mar 25 '15 at 04:55
  • @JonathanLeffler : I guess`[^Z]`match until Z? and if true that scanf would return when Z is pressed? I already [wrote question](http://stackoverflow.com/q/29213463/2284570). Please write an answer on it. – user2284570 Mar 25 '15 at 15:43
  • @user2284570: I don't know enough about DOS and Turbo-C to be willing to try answering your other question. What I once knew I forgot something like fifteen years ago, when the products were clearly archaic and no longer of relevance in the modern world. Suffice to say, trying to match control-Z like that won't work; the character code for control-Z (`'\032'` or `'\x1A'`) is not returned to the program normally when the input is from a terminal. – Jonathan Leffler Mar 25 '15 at 15:54
  • @JonathanLeffler : What about If I try to match a character like`Z`or`A`. Would it cause`scanf()`returning without having to press enter? Also with the embedded market they are no longer archaic. – user2284570 Mar 25 '15 at 16:14
  • @user2284570: No. In general, the terminal saves up a line of input and doesn't send it until the user indicates it should be sent by hitting return (or control-Z). This allows the user to edit what they type. If you want characters as they're typed, you have to use functions not defined by standard C that are specific to the platform you are working on. What you do on DOS with Turbo-C will be different from Windows and MSVC and from Unix/Linux plus curses, etc (and that's before you get GUI window managers into the game). – Jonathan Leffler Mar 25 '15 at 16:43
  • @JonathanLeffler : I just found an implementation for[`ah=0ah int 21h`](http://www.ctyme.com/intr/rb-2563.htm). However I have troubles at understanding how I can modify [this function](http://sourceforge.net/p/freedos/svn/HEAD/tree/kernel/trunk/kernel/chario.c#l360) for [my case](http://stackoverflow.com/q/29213463/2284570). – user2284570 Mar 25 '15 at 16:51
  • @user2284570: No idea; it is not something that is relevant to the platforms where I work. Please stop pestering me on this. I don't know, and don't wish to know, about DOS and Turbo-C. – Jonathan Leffler Mar 25 '15 at 17:09
4

%[^\n] leaves the newline in the buffer. %[^\n]%*c eats the newline character. In any case, %[^\n] can read any number of characters and cause buffer overflow or worse. I use the format string %*[^\n]%*c to gobble the remainder of a line of input from a file. For example, one can read a number and discard the remainder of the line by %d%*[^\n]%*c. This is useful if there is a comment or label following the number, or other data that is not needed.

Vilkoxd
  • 15
  • 3
MarkP
  • 41
  • 1
2
char sen[20];
for (i=0;i<2;i++)
{
  scanf("%[^\n]s",sen);
  printf("%s\n",sen);
  getchar();
}

Hope this helps ... actually "\n" remains in stream input buffer... Ee need to flush it out before scanf is invoked again

Greg
  • 18,111
  • 5
  • 46
  • 68
Code-Zing
  • 41
  • 1
1

I know I am late, but I ran into same problem after testing C after a long time.
The problem here is the new line is considered as input for next iteration.

So, here is my solution, use getchar() to discard the newline the input stream:

char s[10][25];
int i;

for(i = 0; i < 10; i++){
    printf("Enter string: ");
    scanf("%s", s[i]);
    getchar();
}

Hope it helps :)

סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
1

While using scanf("%[^\n]", sen) in a loop, the problem that occurs is that the \n stays within the input buffer and is not flushed. As a result next time, when the same input syntax is used, it reads the \n and considers it as a null input. A simple but effective solution to address this problem is to use:

char sen[20];
for (i=0;i<2;i++)
{
    scanf("%[^\n]%*c",sen);
    printf("%s\n",sen);
}

%*c gets rid of the \n character in the input buffer.