178

Using the following code:

char *name = malloc(sizeof(char) + 256); 

printf("What is your name? ");
scanf("%s", name);

printf("Hello %s. Nice to meet you.\n", name);

A user can enter their name but when they enter a name with a space like Lucas Aardvark, scanf() just cuts off everything after Lucas. How do I make scanf() allow spaces

alk
  • 69,737
  • 10
  • 105
  • 255
Kredns
  • 36,461
  • 52
  • 152
  • 203
  • 11
    Note that more idiomatic is 'malloc(sizeof(char) * 256 + 1)', or 'malloc(256 + 1)', or even better (assuming 'name' will be used strictly locally) 'char name[256+1]'. The '+1' can act as a mneumonic for the null terminator, which needs to be included in the allocation. – Barry Kelly Aug 08 '09 at 04:47
  • @Barry - I suspect `sizeof(char) + 256` was a typo. – Chris Lutz Jul 20 '11 at 22:06

11 Answers11

243

People (and especially beginners) should never use scanf("%s") or gets() or any other functions that do not have buffer overflow protection, unless you know for certain that the input will always be of a specific format (and perhaps not even then).

Remember than scanf stands for "scan formatted" and there's precious little less formatted than user-entered data. It's ideal if you have total control of the input data format but generally unsuitable for user input.

Use fgets() (which has buffer overflow protection) to get your input into a string and sscanf() to evaluate it. Since you just want what the user entered without parsing, you don't really need sscanf() in this case anyway:

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

/* Maximum name size + 1. */

#define MAX_NAME_SZ 256

int main(int argC, char *argV[]) {
    /* Allocate memory and check if okay. */

    char *name = malloc(MAX_NAME_SZ);
    if (name == NULL) {
        printf("No memory\n");
        return 1;
    }

    /* Ask user for name. */

    printf("What is your name? ");

    /* Get the name, with size limit. */

    fgets(name, MAX_NAME_SZ, stdin);

    /* Remove trailing newline, if there. */

    if ((strlen(name) > 0) && (name[strlen (name) - 1] == '\n'))
        name[strlen (name) - 1] = '\0';

    /* Say hello. */

    printf("Hello %s. Nice to meet you.\n", name);

    /* Free memory and exit. */

    free (name);
    return 0;
}
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 1
    I didn't know about `fgets()`. It actually looks easier to use then `scanf()`. +1 – Kredns Aug 08 '09 at 06:37
  • 8
    If you just want to get a line from the user, it is easier. It's also safer since you can avoid buffer overflows. The scanf family is really useful for turning a string into different things (like four chars and an int for example with "%c%c%c%c%d") but, even then, you should be using fgets and sscanf, not scanf, to avoid the possibility of buffer overflow. – paxdiablo Aug 08 '09 at 06:48
  • 5
    You can put maximum buffer size in scanf format, you just can't put runtime computed one without building the format at runtime (there isn't the equivalent of * for printf, * is a valid modificator for scanf with another behavior: suppressing assignation). – AProgrammer Aug 08 '09 at 11:55
  • Note also that `scanf` has undefined behavior if numeric conversion overflows ([N1570 7.21.6.2p10](http://port70.net/~nsz/c/c11/n1570.html#7.21.6.2p10), last sentence, wording unchanged since C89) which means _none_ of the `scanf` functions can safely be used for numeric conversion of untrusted input. – zwol Apr 30 '19 at 18:07
  • 1
    @JonathanKomar and anyone else reading this in the future: if your professor told you you had to use `scanf` in an assignment, they were wrong to do so, and you may tell them I said so, and if they want to argue with me about it, my email address is easily found from my profile. – zwol Apr 30 '19 at 18:10
  • Note that writing `name[strcspn(name, "\n")] = '\0';` works correctly for all data read by `fgets()` when it did not return a NULL pointer (indicate EOF or error) — without need for conditionals in your code. – Jonathan Leffler Jul 12 '19 at 08:25
  • Why do we need to control user input string for '\n'? I can't understand it – Hasan Basri Aug 20 '20 at 21:41
  • @HasanBasri - If the intent of your question is: _"why does sting input need to be stopped when newline character is seen?"_. Its simply because the design of `fgets()` dictates it. (i.e. char `fgets (char Line_Buffer[], int Number_of_Chars, FILE *Stream);`: _"Reads characters from the specified input stream into a lineBuffer until end-of-file is encountered, a newline character is read, or (number_ofChars - 1) characters are read."_) If a different behavior is desired, write your own variation of a read function, eg `my_fgets()` that does not include that behavior. – ryyker Sep 09 '21 at 13:03
  • But I don't like `fgets`. Its name is so ugly. – NeoZoom.lua Mar 14 '22 at 09:39
  • 1
    @Rainning: it's not meant to be beautiful, otherwise we wouldn't have `strcscnp` or `atoi`. It's a functional name probably meant to indicate `gets` from a file handle. It's ability to do what the OP wanted is by no means affected by its name. – paxdiablo Mar 14 '22 at 10:04
  • @paxdiablo: You're right. But as a new learner to C, I expect something more readable without too many `#include`s that I have never seen before..., so I prefer `%[^\n]`. (and I cannot upvote after reading your kindly comment.) – NeoZoom.lua Mar 14 '22 at 11:08
  • @Rainning: just to be clear, `%[^\n]` has *exactly* the same issue as `gets()` (which ISO wisely removed from the standard because there's no way to use it safely). That issue is buffer overflow. You should not sacrifice safety for readability. – paxdiablo Mar 15 '22 at 02:22
148

Try

char str[11];
scanf("%10[0-9a-zA-Z ]", str);
starball
  • 20,030
  • 7
  • 43
  • 238
Kelly Robins
  • 7,168
  • 6
  • 43
  • 66
  • 13
    (1) Obviously to accept spaces, you need to put a space in the character class. (2) Note that the 10 is the maximum number of characters which will be read, so str has to point to a buffer of size 11 at least. (3) The final s here isn't a format directive but scanf will try here to match it exactly. The effect will be visible on an entry like 1234567890s where the s will be consumed but put no where. An other letter won't be consumed. If you put another format after the s, it will be read only if there is an s to be matched. – AProgrammer Aug 08 '09 at 11:52
  • 1
    Another potential problem, the use of - at other place than the first or the last is implementation defined. Usually, it is used for ranges, but what the range designate is dependent on the charset. EBCDIC has holes in the letter ranges and even when assuming an ASCII derived charsets it is naïve to think that all lower case letters are in the a-z range... – AProgrammer Aug 08 '09 at 13:35
  • 3
    "%[^\n]" has the same problem as gets(), buffer overflow. With the additional catch that the \n final isn't read; this will be hidden by that fact that most formats start by skipping white spaces, but [ isn't one of them. I don't understand the instance at using scanf to read strings. – AProgrammer Aug 09 '09 at 06:09
  • 4
    Removed the `s` from the end of the input string since it's both superfluous and _incorrect_ in certain cases (as pointed out in earlier comments). `[` is it's _own_ format specifier rather than some variation of the `s` one. – paxdiablo Feb 15 '15 at 06:23
69

This example uses an inverted scanset, so scanf keeps taking in values until it encounters a '\n'-- newline, so spaces get saved as well

#include <stdio.h>

int main (int argc, char const *argv[])
{
    char name[20];

    // get up to buffer size - 1 characters (to account for NULL terminator)
    scanf("%19[^\n]", name);
    printf("%s\n", name);
    return 0;
}
david8
  • 444
  • 9
  • 23
SVA
  • 891
  • 1
  • 6
  • 6
29

You can use this

char name[20];
scanf("%19[^\n]", name);

Or this

void getText(char *message, char *variable, int size){
    printf("\n %s: ", message);
    fgets(variable, sizeof(char) * size, stdin);
    sscanf(variable, "%[^\n]", variable);
}

char name[20];
getText("Your name", name, 20);

DEMO

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
Vitim.us
  • 20,746
  • 15
  • 92
  • 109
  • 1
    Just as a side note, `sizeof(char)` is by definition *always* 1, so there's no need to multiply by it. – paxdiablo Mar 22 '16 at 07:37
  • @paxdiablo I don't think this is true for all architectures/platforms – Vitim.us Feb 27 '21 at 23:50
  • 2
    @Vitim.us: if you're referring to my `sizeof(char) == 1` comment, it's mandated by the standard. See, for example, `C11 6.5.3.4 /4`: "When `sizeof` is applied to an operand that has type `char`, `unsigned char`, or `signed char`, (or a qualified version thereof) the result is `1`". Some people make the mistake that, since `sizeof` returns the number of bytes in a type/variable, a 16-bit `char` will give two. But that's not the case since the standard doesn't define "byte" as eight bits, rather it defines it as "a contiguous sequence of bits, the number of which is implementation-defined". – paxdiablo Feb 28 '21 at 01:26
  • `sscanf(variable, "%[^\n]", variable);` looks like a fishy way to remove `\n`. As `buffer` in`int sscanf( const char *restrict buffer, const char *restrict format, ... );` is `restruct` I don't think you should do that. – Ted Lyngmo Jul 17 '23 at 05:31
10

Don't use scanf() to read strings without specifying a field width. You should also check the return values for errors:

#include <stdio.h>

#define NAME_MAX    80
#define NAME_MAX_S "80"

int main(void)
{
    static char name[NAME_MAX + 1]; // + 1 because of null
    if(scanf("%" NAME_MAX_S "[^\n]", name) != 1)
    {
        fputs("io error or premature end of line\n", stderr);
        return 1;
    }

    printf("Hello %s. Nice to meet you.\n", name);
}

Alternatively, use fgets():

#include <stdio.h>

#define NAME_MAX 80

int main(void)
{
    static char name[NAME_MAX + 2]; // + 2 because of newline and null
    if(!fgets(name, sizeof(name), stdin))
    {
        fputs("io error\n", stderr);
        return 1;
    }

    // don't print newline
    printf("Hello %.*s. Nice to meet you.\n", strlen(name) - 1, name);
}
Christoph
  • 164,997
  • 36
  • 182
  • 240
7

getline()

Now part of POSIX, none-the-less.

It also takes care of the buffer allocation problem that you asked about earlier, though you have to take care of freeing the memory.

dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
  • Standard? In the reference you cite: "Both getline() and getdelim() are GNU extensions." – AProgrammer Aug 09 '09 at 08:59
  • 1
    POSIX 2008 adds getline. So GNU wen ahead and changed their headers for glibc around version 2.9, and it is causing trouble for many projects. Not a definitive link, but look here: https://bugzilla.redhat.com/show_bug.cgi?id=493941 . As for the on-line man page, I grabbed the first one google found. – dmckee --- ex-moderator kitten Aug 09 '09 at 14:27
6

You can use the fgets() function to read a string or use scanf("%[^\n]s",name); so string reading will terminate upon encountering a newline character.

jonsca
  • 10,218
  • 26
  • 54
  • 62
Anshul garg
  • 233
  • 2
  • 6
5

If someone is still looking, here's what worked for me - to read an arbitrary length of string including spaces.

Thanks to many posters on the web for sharing this simple & elegant solution. If it works the credit goes to them but any errors are mine.

char *name;
scanf ("%m[^\n]s",&name);
printf ("%s\n",name);
0

You may use scanf for this purpose with a little trick. Actually, you should allow user input until user hits Enter (\n). This will consider every character, including space. Here is example:

int main()
{
  char string[100], c;
  int i;
  printf("Enter the string: ");
  scanf("%s", string);
  i = strlen(string);      // length of user input till first space
  do
  {
    scanf("%c", &c);
    string[i++] = c;       // reading characters after first space (including it)
  } while (c != '\n');     // until user hits Enter
  string[i - 1] = 0;       // string terminating
return 0;
}

How this works? When user inputs characters from standard input, they will be stored in string variable until first blank space. After that, rest of entry will remain in input stream, and wait for next scanf. Next, we have a for loop that takes char by char from input stream (till \n) and apends them to end of string variable, thus forming a complete string same as user input from keyboard.

Hope this will help someone!

akelec
  • 3,797
  • 3
  • 41
  • 39
-1

While you really shouldn't use scanf() for this sort of thing, because there are much better calls such as gets() or getline(), it can be done:

#include <stdio.h>

char* scan_line(char* buffer, int buffer_size);

char* scan_line(char* buffer, int buffer_size) {
   char* p = buffer;
   int count = 0;
   do {
       char c;
       scanf("%c", &c); // scan a single character
       // break on end of line, string terminating NUL, or end of file
       if (c == '\r' || c == '\n' || c == 0 || c == EOF) {
           *p = 0;
           break;
       }
       *p++ = c; // add the valid character into the buffer
   } while (count < buffer_size - 1);  // don't overrun the buffer
   // ensure the string is null terminated
   buffer[buffer_size - 1] = 0;
   return buffer;
}

#define MAX_SCAN_LENGTH 1024

int main()
{
   char s[MAX_SCAN_LENGTH];
   printf("Enter a string: ");
   scan_line(s, MAX_SCAN_LENGTH);
   printf("got: \"%s\"\n\n", s);
   return 0;
}
Nissa
  • 4,636
  • 8
  • 29
  • 37
Ed Zavada
  • 53
  • 2
  • 3
    There's a *reason* why `gets` was deprecated and removed (https://stackoverflow.com/questions/30890696/why-gets-is-deprecated) from the standard. It's even *worse* that `scanf` because at least the latter has ways to make it safe. – paxdiablo Nov 10 '17 at 01:25
  • [Why is the gets function so dangerous that it should not be used?](https://stackoverflow.com/q/1694036/995714) `gets` have been deprecated from C99, long before 2016. It has been removed completely from C11 – phuclv Feb 06 '21 at 04:36
-2
/*reading string which contains spaces*/
#include<stdio.h>
int main()
{
   char *c,*p;
   scanf("%[^\n]s",c);
   p=c;                /*since after reading then pointer points to another 
                       location iam using a second pointer to store the base 
                       address*/ 
   printf("%s",p);
   return 0;
 }
  • 6
    Can you explain why this is the correct answer? Please do not post code-only answers. – Theo May 15 '19 at 14:02
  • 1
    `s` doesn't belong there after the scanset – Antti Haapala -- Слава Україні Jun 27 '19 at 05:42
  • 1
    `c` is not initialised. In case of successful scanning this writes the scanned value in unpredictable, not legally accessable memory. Printing from a copy of that rogue pointer does not help at all. If you have "successfully" tested this then you were very (un-)lucky with the outcome of undefined behaviour. This code (explained or not) is not the answer to any question. – Yunnosch Mar 14 '22 at 10:15