-6

Is it possible to read an entire string including blank spaces like gets() function in scanf()?

I am able to do it using the gets() function.

char s[30];
gets(s);

This will read a line of characters. Can this be done in scanf()?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    One of your howmeworks I presume? – Vimal Bhaskar Jan 06 '17 at 05:35
  • 1
    You *can* use [`scanf`](http://en.cppreference.com/w/c/io/fscanf) to read a line, but it's not the best tool for it. Instead I suggest you use [`fgets`](http://en.cppreference.com/w/c/io/fgets). ***Don't*** use `gets`. It is a dangerous and bad function. It has also been deprecated in the C99 standard, and removed completely from the C11 standard. – Some programmer dude Jan 06 '17 at 05:36
  • @SeekAddo Your [`scanf("%30[^\n]", s);`](http://stackoverflow.com/questions/41499535/scanf-clarification-in-c-language#comment70204953_41499535) uses the wrong width limitation and has the same problem as in this [answer](http://stackoverflow.com/questions/41499535/scanf-clarification-in-c-language#comment70204925_41499577) – chux - Reinstate Monica Jan 06 '17 at 05:46
  • You can use `scanf()`, likely incorrectly, as many or heed [@Some programmer dude](http://stackoverflow.com/questions/41499535/scanf-clarification-in-c-language#comment70204787_41499535) good advice. – chux - Reinstate Monica Jan 06 '17 at 05:49
  • @SeekAddo `getline()` is not part of the standard C library. With `stdin`, it is also susceptible to a hacker exploit of overwhelming memory as it does not enforce even a sane limit of memory usage. – chux - Reinstate Monica Jan 06 '17 at 05:52
  • Why the down vote? I am trying to learn. This is a forum where doubts can be cleared. – user7374011 Jan 06 '17 at 06:05
  • Please note that there is [no safe way to use `gets()`](http://stackoverflow.com/questions/1694036/why-is-the-gets-function-dangerous-why-should-it-not-be-used). – Jonathan Leffler Jan 06 '17 at 06:48
  • @user7374011 Concerning [This is a forum where doubts can be cleared](http://stackoverflow.com/questions/41499535/scanf-clarification-in-c-language/41499577?noredirect=1#comment70205453_41499535), You may want to review this post [Is Stack Overflow a forum?](http://meta.stackexchange.com/questions/92107/is-stack-overflow-a-forum). Also on the [question page](http://stackoverflow.com/questions/ask), you may have noticed "How to Ask Is your question about programming? We prefer questions that can be answered, not just discussed." – chux - Reinstate Monica Jan 06 '17 at 15:37

2 Answers2

2

You can read a line, including blank spaces, with scanf(), but this function is subtle, and using it is very error-prone. Using the %[^\n] conversion specifier, you can tell scanf() to match characters to form a string, excluding '\n' characters. If you do this, you should specify a maximum field width. This width specifies the maximum number of characters to match, so you must leave room for the '\0' terminator.

It is possible that the first character in the input stream is a '\n'. In this case, scanf() would return a value of 0, since there were no matches before encountering the newline. But, nothing would be stored in s, so you may have undefined behavior. To avoid this, you can call scanf() first using the %*[\n] conversion specifier, discarding any leading '\n' characters.

After the string has been read, there will be additional characters in the input stream. At least a '\n' is present, and possibly more characters if the user entered more than the maximum field width specifies. You might then want to discard these extra characters so that they don't interfere with further inputs. The code below includes a loop to do this operation.

The first call to scanf() will consume all newline characters in the input stream until a non-newline character is encountered. While I believe that the second call to scanf() should always be successful, it is good practice to always check the return value of scanf() (which is the number of successful assignments made). I have stored this value in result, and check it before printing the string. If scanf() returns an unexpected result, an error message is printed.

It is better, and easier, to use fgets() to read entire lines. You must remember that fgets() keeps the trailing newline, so you may want to remove it. There is also a possibility that the user will enter more characters than the buffer will store, leaving the remaining characters in the input stream. You may want to remove these extra characters before prompting for more input.

Again, you should check the return value of fgets(); this function returns a pointer to the first element of the storage buffer, or a NULL pointer in the event of an error. The code below replaces any trailing newline character in the string, discards extra characters from the input stream, and prints the string only if the call to fgets() was successful. Otherwise, an error message is printed.

#include <stdio.h>

int main(void)
{
    char s[30];
    int result;

    printf("Please enter a line of input:\n");
    scanf("%*[\n]");                // throw away leading '\n' if present
    result = scanf("%29[^\n]", s);  // match up to 29 characters, excluding '\n'

    /* Clear extra characters from input stream */
    int c;
    while ((c = getchar()) != '\n' && c != EOF)
        continue;                   // discard extra characters

    if (result == 1) {
        puts(s);
    } else {
        fprintf(stderr, "EOF reached or error in scanf()\n");
    }

    printf("Please enter a line of input:\n");    
    char *ps = fgets(s, 30, stdin); // keeps '\n' character

    if (ps) {
        while (*ps && *ps != '\n') {
            ++ps;
        }
        if (*ps) {                  // replace '\n' with '\0'
            *ps = '\0';
        } else {
            while ((c = getchar()) != '\n' && c != EOF)
                continue;           // discard extra characters
        }

        puts(s);

    } else {
        fprintf(stderr, "EOF reached or error in fgets()\n");
    }

    return 0;
}

Note that these two methods of getting a line of input are not exactly equivalent. The scanf() method, as written here, does not accept an empty line (i.e., a line consisting of only the '\n' character), but does accept lines consisting of other whitespace characters. The fscanf() method will accept an empty line as input.

Also, if it is acceptable to ignore leading whitespace characters, it would be simpler to follow the recommendation given by Jonathan Leffler in the comments to use only a single call to scanf():

result = scanf(" %29[^\n]", s);

This will ignore leading whitespace characters, including newlines.

ad absurdum
  • 19,498
  • 5
  • 37
  • 60
  • 1
    If leading blanks or tabs are not significant, it is simpler to use `scanf(" %29[^\n", s)` rather than two calls to `scanf()`. You should be checking that the second `scanf()` read anything successfully. I think I'd create `static inline void gobble(void) { int c; while ((c = getchar()) != EOF && c != '\n') ; }` and use that twice rather than write the loop out twice. – Jonathan Leffler Jan 06 '17 at 06:55
  • @JonathanLeffler-- It was not clear to me whether leading blank spaces were wanted in the string or not, hence my clunky `scanf()` solution with two calls. Ordinarily I would remove the stream-clearing code to a function, but I decided not to here since there are two different approaches being illustrated. No good excuse for not checking the return from `scanf()`; that was still nagging at me. I think that it always must be 1; well I suppose there could be an error in the function. I will edit. – ad absurdum Jan 06 '17 at 07:08
  • Yours is a better answer. Note: 1) Suggest `"Input error in scanf()"` --> `"End-of-file or input error in scanf()"` 2) leading `scanf("%*[\n]");` disallows entering a "blank" line. – chux - Reinstate Monica Jan 06 '17 at 15:43
  • @chux-- I have improved the error message. You are right that the `scanf()` and `fgets()` methods behave differently for blank lines; I have added some comments pointing this out at the end of my answer. Thanks... good suggestions. – ad absurdum Jan 06 '17 at 15:57
1

Do not use scanf() or gets() function — use fgets() instead. But for the above question please find the answer.

int main() {
    char a[30];
    scanf ("%29[^\n]%*c", name);
    printf("%s\n", a);
    return 0;
}

Its also highly recommended like I told in the beginning to use fgets() instead. We clearly do not understand the weird requirement. I would have used the fgets() to read the character.

fgets(a, size(a), stdin);
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Vimal Bhaskar
  • 748
  • 1
  • 5
  • 17
  • 2
    Using `scanf("%[^\n]", a);`, if the first character attempted to be read is `'\n'`, this leads to UB with `printf("%s", a);`. Better to check the return value of `scanf()`. – chux - Reinstate Monica Jan 06 '17 at 05:43
  • `scanf ("%[^\n]%*c", name);` has the same [problem](http://stackoverflow.com/questions/41499535/scanf-clarification-in-c-language#comment70204925_41499577). – chux - Reinstate Monica Jan 06 '17 at 05:47
  • Have checked it with my compiler and I am unable to recreate the issue, Also i hate doing it with scanf. I have just given the user a way to do it. I am gonna edit my post as to how this can be done in fgets() – Vimal Bhaskar Jan 06 '17 at 05:49
  • 1
    If your program is `a.out`, then running `a.out < /dev/null` on a Unix-like system gives you undefined behaviour; the array `a` is not initialized with any determinate value. – Jonathan Leffler Jan 06 '17 at 06:51
  • 1
    To re-create issue: `scanf ("%29[^\n]%*c", a); for (int i=0;i<3; i++) printf(" %d", getchar());` and type ` '0' `. Is your output `" 10 48 10"`? If so what are the contents of `a`? (Undefined?) If causes `'\r'` and `'\n'` you will not see this. – chux - Reinstate Monica Jan 06 '17 at 15:26