$Id: why_not_scanf.txt,v 1.1 2013/04/28 17:58:02 db Exp db $ Why scanf sucks Occasionally the beginning C programmer is tempted to use scanf for decoding an input stream. Here's a typical naive attempt. scanf("%s %d",string, &i); Typically the beginner is mystified when the terminal appears to hang without returning the expected string and integer. The format string has forgotten to account for the newline still in the buffer, so the sscanf gets confused. Moreover, different systems have different ideas about what should be in the buffer on a carriage return as well. If your incompetent teacher insists you have to use sscanf() you could try a getchar(); or two to flush the input before trying another scanf, but by far the safest bet is to use an fgets(); then parse the string yourself. The fgets will smoothly pull the newline out for you. No fuss, no muss. Now that you have replaced that scanf() with a fgets() The second major pitfall is assuming that you can now safely use a sscanf() fgets(input_buf, sizeof(input_buf) - 1, stdin); sscanf(input_buf,"%s %d", string, &i); This is certainly better but still error prone. What if your input consists of two spaces not just one? What if the input is a string and a float? You are far better off doing something like /* * Sample program to illustrate how to parse a simple * input from user * * Diane Bruce 2012 */ #include #include #include #include #include #include #include #include #include #define MAXLINE 256 int valid_int(char *s); int main(void) { char input_buf[MAXLINE]; char *string; char *token; char *p; char *q; int i; fgets(input_buf, sizeof(input_buf) - 1, stdin); p = input_buf; if ((q = strchr(p,'\n')) != NULL) /* strip any newline */ *q = '\0'; token = strsep(&p, " "); if (token == NULL || *token == '\0') errx(EX_DATAERR,"Missing string"); string = strdup(token); assert(string != NULL); /* Now deal with integer */ token = strsep(&p, " "); if (token == NULL || *token == '\0') errx(EX_DATAERR,"Missing integer"); if (valid_int(token)) i = strtol(token, NULL, 10); else errx(EX_DATAERR,"Garbled integer"); printf("string = %s integer = %d\n", string, i); } /* * valid_int * * inputs - string * output - 1 if valid int , 0 if not * side effects - none */ int valid_int(char *s) { while (*s != '\0') { if (*s != '-' && !isdigit(*s)) return(0); s++; /* N.B. isdigit is often a macro */ } return (1); } ... The slightly more advanced C coder may be tempted to use regular expressions in their sscanf(), after all it works in perl ever so well. Don't be tempted. It is far safer to painfully parse as I have shown than risk an error due to an unexpected match in a R.E. you have missed. This author has seen this happen in one large IRC network, where the entire network was KLINED (every single user kicked off) due to an unexpected match in a sscanf(). The network will remain unnamed, the coders faces are red enough. Note that sometimes you are forced to use sscanf(), floats are a large exception. sscanf("%f", float_string); All I can say here is make very sure float_string is sanitized so the only thing it can contain is a float.