Our task for school lesson is to input a number of integers. We don't know how many there will be.
I would like to know if there is a possibility to format scanf function that either stores integer or terminate itself by pressing enter.
Can I somehow put together scanf("%d") which only stores integers and scanf("%[^\n]) which terminates scanf function?
What I have known yet is that I cannot use scanf("%d%[^\n]) because scanf is waiting for that one integer, which I don't want to input because I already stored all integers I had to.
I don't really like a possibility to store string of all those integers into an array and then convert it to elements of another array with the exact numbers.
The scanf function is difficult to use correctly.
Instead, read a line at a time with fgets. If the entered string is just a newline, you exit the loop. If not, use strtol to parse the value. You'll know if just an integer was entered if the end pointer points to the newline at the end of the input.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
char line[100], *p;
long val;
while (fgets(line, sizeof(line), stdin)) {
// if enter was pressed by itself, quit loop
if (!strcmp(line, "\n")) {
break;
}
errno = 0;
val = strtol(line, &p, 10);
if (errno) {
perror("error reading value");
} else if ((p != line) && (*p == '\n')) {
// a valid integer was read
printf("read value %ld\n", val);
} else {
// a non-integer was read or extra characters were entered
printf("not a valid integer: %s", line);
}
}
return 0;
}
Scan a character. Skip space and tab. Exit on newline.
Unget most recent character and try to scan an integer. If unable to scan an integer, scan and discard non-digit except space tab and newline.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main ( void) {
char i = '\0';
int value = 0;
int result = 0;
printf ( "type number separated by space then enter\n");
while ( 1 == scanf("%c",&i)) {//scan a character
if ( ' ' == i || '\t' == i) {
continue;
}
if ( i == '\n') {
break;//newline so leave loop
}
ungetc ( i, stdin);//replace the character in input stream
if ( 1 == ( result = scanf ( "%d", &value))) {
printf ( " number entered as %d\n", value);
}
else {
scanf ( "%*[^0-9 \t\n]");//clean non digits except space tab newline
//or you could just break;
}
}
return 0;
}
Related
When I do not use any scanset or a negated scanset, encountering which character in the buffer will make scanf() stop reading data from the buffer (assuming that the character array is large enough)? It seemed as if the answer is a blank because if we enter a multi-worded sentence in the output, then by default scanf() stores only the first word but when i run this code:
int main(void) {
char n[20];
int status;
do {
status = scanf("%[^ ]", n);
//status = scanf("%s", n);
printf("%d %s\n", status, n);
} while (status);
return 0;
}
and enter hello world as the input and press the return key, the output is:
1 hello
0 hello
and when I change the do-while to:
do {
//status = scanf("%[^ ]", n);
status = scanf("%s", n);
printf("%d %s\n", status, n);
} while (status);
I get this as the output (for the same input):
1 hello
1 world
and the program failed to terminate on its own
Again, for the first do-while, if I enter hello and press enter, I do not get any output and program doesn't terminate (obviously because it read no space). But this is not the case for the second do-while.
Also it would be great if someone could answer how do I change my code to make it terminate upon pressing the return key, all the while storing one word at a time.
By default, reading which character in the input buffer makes scanf() stop reading a string?
There is no default.
When/how scanf() stops depends on the format. Reading will also stop when end-of-file is encountered or an input error.
// format "%c"
// Reads 1 character, then stops, no special value stops it early.
char ch;
scan("%c", &ch);
// format "%9[^ ]"
// Reads up to 9 non-space characters, then stops. Even reads a null character.
char buf[10];
scan("%9[^ ]", buf);
// format "%9s"
// Reads and discards leading white-space characters,
// then reads/saves up to 9 non-white-space characters, then stops.
// Even reads a null character.
char buf[10];
scan("%9s", buf);
// format "xyz"
// Reads up to 3 characters that match the pattern "xyz", then stops.
scan("xyz");
Capturing enter with scanf is difficult. Using fgets it is easy to see if a blank line has been entered by checking index [0]. Use sscanf to parse each word. The %n specifier will tell you how many characters were used in the scan. That value can be added to the pointer temp to parse each word.
#include <stdio.h>
#include <stdlib.h>
int main() {
char n[256] = { '\0'};
char word[256] = { '\0'};
char *temp = NULL;
int result = 0;
int chs = 0;
while ( 1) {
printf("\nEnter text(enter only to exit)\n");
fgets ( n, sizeof n, stdin);
if ( n[0] == '\n') {
break;
}
temp = n;
while ( 1 == ( result = sscanf ( temp, "%255s%n", word, &chs))) {
temp += chs;
printf ( "%s\n", word);
}
}
return 0;
}
Another approach would be to read each character using getchar. Check for a newline to exit, check for whitespace to print each word and check for printable characters to accumulate into each word.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define LIMIT 79
int main( void) {
char n[LIMIT + 1] = { '\0'};
int at = 0;
int ch = 0;
printf ( "type the text and press enter\n");
while ( ( ch = getchar ( )) != EOF) {
if ( ch == '\n') {
if ( at) {
fputs ( n, stdout);
fputc ( '\n', stdout);
}
at = 0;
break;
}
if ( at >= LIMIT || isspace ( ch)) {
if ( at) {
fputs ( n, stdout);
fputc ( '\n', stdout);
}
at = 0;
}
if ( isgraph ( ch)) {
n[at] = ch;
at++;
n[at] = '\0';
}
}
if ( at) {
fputs ( n, stdout);
fputc ( '\n', stdout);
}
return 0;
}
For a negated scanset, scanf() stops reading when it gets a byte from stdin that matches the scanset (a space for your %[^ ] format) or when it reaches the end of file.
Note that if it reads a matching byte or the end of file right away, the conversion fails and scanf() returns 0 or EOF respectively.
You should change your code to avoid passing the n array to printf() if the conversion failed as it would be uninitialized and the behavior in printf() would be undefined.
You should also prevent a potential buffer overflow by passing the maximum number of characters to store into n before the null terminator.
Here is a safer version:
#include <stdio.h>
int main(void) {
char n[20];
int status;
while ((status = scanf("%19[^ ]", n)) == 1) {
printf("%d %s\n", status, n);
}
printf("stop with status=%d\n", status);
return 0;
}
The problem with the above loop is the second iteration will fail because the space is still pending in stdin.
You can fix this by adding an initial ' ' in the format string to skip initial white space before the conversion: scanf(" %19[^ ]", n), but this would skip other white space such as newlines.
you should probably stop the scanf on newline characters as well
A stricter solution is to skip the pending spaces after each successful conversion:
#include <stdio.h>
int main(void) {
char n[20];
int status;
while ((status = scanf("%19[^ \n]", n)) == 1) {
printf("%d %s\n", status, n);
scanf("%*[ \n]"); // skip spaces
}
printf("stop with status=%d\n", status);
return 0;
}
If you want to exit the loop if the user presses enter on an empty line, you can test for this first and unget the byte if it is not a newline:
#include <stdio.h>
int main(void) {
char n[20];
int c, status;
for (;;) {
if ((c = getchar()) == EOF || c == '\n')
break;
ungetc(c, stdin);
if ((status = scanf("%19[^ \n]", n)) == 1) {
printf("%d %s\n", status, n);
scanf("%*c"); // skip the space or newline that stopped the scanf
} else {
printf("stop with status=%d\n", status);
break;
}
}
return 0;
}
#include <stdio.h>
#include <string.h>
int main() {
char a[100], b[100];
char *ret;
scanf("%[^\n]s", a);
scanf("%[^\n]s", b);
ret = strstr(a, b);
if (ret != NULL)
printf("its a substring");
else
printf("not a substring");
return 0;
}
My aim was to check whether a substring is present in the parent string in the string or not. I learned about the strstr() function from here.
I have previously used %[^\n]s in my codes before and they worked well.
But, in this case as soon as I hit return/enter after typing one string, the output is not a substring.
What is it that I am doing wrong?
The first call to scanf() stops when it sees a newline ('\n') but it's still in the input stream.
So, the second call fails immediately as it sees the (same) newline character.
You should always check the return value of scanf() calls for failure.
You can insert a getchar(); call in between the scanf() calls to consume the newline character. Or better use fgets() and handle the newline character.
This is how you could use fgets() in your code:
#include <stdio.h>
#include <string.h>
int main(void) {
char a[100], b[100];
char *ret;
if (fgets(a, sizeof a, stdin) == NULL) {
/* error handling */
}
a[strcspn(a, "\n")] = '\0';
if (fgets(b, sizeof b, stdin) == NULL) {
/* error handling */
}
b[strcspn(b, "\n")] = '\0';
ret=strstr(a, b);
if(ret!=NULL)
printf("its a substring");
else
printf("not a substring");
return 0;
}
The scanf() format you use %[^\n] does not have a trailing s and you should provide the maximum number of characters to store into the destination array. Since the array has a size of 100, you should specify %99[^\n].
Furthermore, you must skip the newline between the 2 %99[^\n] otherwise the second conversion will fail since there would no characters different from \n to match.
Here is a simple correction using scanf(). usr has a good alternative using fgets() which is a simpler way to parse input:
#include <stdio.h>
#include <string.h>
int main(void) {
char a[100], b[100], c;
if (scanf("%99[^\n]%c%99[^\n]", a, &c, b) == 3 && c == '\n') {
if (strstr(a, b))
printf("its a substring\n");
else
printf("not a substring\n");
}
return 0;
}
This question already has an answer here:
How to read / parse input in C? The FAQ
(1 answer)
Closed 6 years ago.
I am trying to write a simple program which will read two input lines, an integer followed by a string. However, it doesn't seem to work for me.
int main()
{
int i;
char str[1024];
scanf("%d", &i);
scanf("%[^\n]", str);
printf("%d\n", i);
printf("%s\n", str);
return 0;
}
Immediately after entering the integer and pressing "Enter", the program prints the integer. It doesn't wait for me to enter the string. Whats wrong? Whats the correct way to program this?
What you need to know
The problem with %[^\n] is that it fails when the first character to be read is the newline character, and pushes it back into the stdin.
The Problem
After you enter a number for the first scanf, you press Enter. %d in the first scanf consumes the number, leaving the newline character ('\n'), generated by the Enter keypress, in the standard input stream (stdin). %[^\n] in the next scanf sees this \n and fails for the reason given in the first paragraph of this answer.
Fixes
Solutions include:
Changing scanf("%d", &i); to scanf("%d%*c", &i);. What %*c does is, it scans and discards a character.
I wouldn't recommend this way because an evil user could trick the scanf by inputting something like <number><a character>\n, ex: 2j\n and you'll face the same problem again.
Adding a space (any whitespace character will do) before %[^\n], i.e, changing scanf("%[^\n]", str); to scanf(" %[^\n]", str); as #Bathsheba mentioned in a comment.
What the whitespace character does is, it scans and discards any number of whitespace characters, including none, until the first non-whitespace character.
This means that any leading whitespace characters will be skipped when inputting for the second scanf.
This is my recommendation: Clear the stdin after every scanf. Create a function:
void flushstdin(void)
{
int c;
while((c = getchar()) != '\n' && c != EOF);
}
and call it after every scanf using flushstdin();.
Other issues:
Issues unrelated to your problem include:
You don't deal with the case if scanf fails. This can be due to a variety of reasons, say, malformed input, like inputting an alphabet for %d.
To do this, check the return value of scanf. It returns the number of items successfully scanned and assigned or -1 if EOF was encountered.
You don't check for buffer overflows. You need to prevent scanning in more than 1023 characters (+1 for the NUL-terminator) into str.
This can be acheived by using a length specifier in scanf.
The standards require main to be declared using either int main(void) or int main(int argc, char* argv[]), not int main().
You forgot to include stdio.h (for printf and scanf)
Fixed, Complete Program
#include <stdio.h>
void flushstdin(void)
{
int c;
while((c = getchar()) != '\n' && c != EOF);
}
int main(void)
{
int i;
char str[1024];
int retVal;
while((retVal = scanf("%d", &i)) != 1)
{
if(retVal == 0)
{
fputs("Invalid input; Try again", stderr);
flushstdin();
}
else
{
fputs("EOF detected; Bailing out!", stderr);
return -1;
}
}
flushstdin();
while((retVal = scanf("%1023[^\n]", str)) != 1)
{
if(retVal == 0)
{
fputs("Empty input; Try again", stderr);
flushstdin();
}
else
{
fputs("EOF detected; Bailing out!", stderr);
return -1;
}
}
flushstdin();
printf("%d\n", i);
printf("%s\n", str);
return 0;
}
This simply, will work:
scanf("%d %[^\n]s", &i, str);
Instaed of scanf() use fgets() followed by sscanf().
Check return values of almost all functions with a prototype in <stdio.h>.
#include <stdio.h>
int main(void) {
int i;
char test[1024]; // I try to avoid identifiers starting with "str"
char tmp[10000]; // input buffer
// first line
if (fgets(tmp, sizeof tmp, stdin)) {
if (sscanf(tmp, "%d", &i) != 1) {
/* conversion error */;
}
} else {
/* input error */;
}
// second line: read directly into test
if (fgets(test, sizeof test, stdin)) {
size_t len = strlen(test);
if (test[len - 1] == '\n') test[--len] = 0; // remove trailing ENTER
// use i and test
printf("i is %d\n", i);
printf("test is \"%s\" (len: %d)\n", test, (int)len);
} else {
/* input error */;
}
return 0;
}
I created a program to make a diamond out of *'s. I am looking for a way to check if the type of input is an integer in the C language. If the input is not an integer I would like it to print a message.
This is what I have thus far:
if(scanf("%i", &n) != 1)
printf("must enter integer");
However it does not display the message if it's not an integer. Any help/guidance with this issue would be greatly appreciated!
you can scan your input in a string then check its characters one by one, this example displays result :
0 if it's not digit
1 if it is digit
you can play with it to make your desired output
char n[10];
int i=0;
scanf("%s", n);
while(n[i] != '\0')
{
printf("%d", isdigit(n[i]));
i++;
}
Example:
#include <stdio.h>
#include <string.h>
main()
{
char n[10];
int i=0, flag=1;
scanf("%s", n);
while(n[i] != '\0'){
flag = isdigit(n[i]);
if (!flag) break;
i++;
}
if(flag)
{
i=atoi(n);
printf("%d", i);
}
else
{
printf("it's not integer");
}
}
Use fgets() followed by strtol() or sscanf(..."%d"...).
Robust code needs to handle IO and parsing issues. IMO, these are best done separately.
char buf[50];
fgets(buf, sizeof buf, stdin);
int n;
int end = 0; // use to note end of scanning and catch trailing junk
if (sscanf(buf, "%d %n", &n, &end) != 1 || buf[end] != '\0') {
printf("must enter integer");
}
else {
good_input(n);
}
Note:
strtol() is a better approach, but a few more steps are needed. Example
Additional error checks include testing the result of fgets() and insuring the range of n is reasonable for the code.
Note:
Avoid mixing fgets() and scanf() in the same code.
{ I said scanf() here and not sscanf(). }
Recommend not to use scanf() at all.
strtol
The returned endPtr will point past the last character used in the conversion.
Though this does require using something like fgets to retrieve the input string.
Personal preference is that scanf is for machine generated input not human generated.
Try adding
fflush(stdout);
after the printf. Alternatively, have the printf output a string ending in \n.
Assuming this has been done, the code you've posted actually would display the message if and only if an integer was not entered. You don't need to replace this line with fgets or anything.
If it really seems to be not working as you expect, the problem must be elsewhere. For example, perhaps there are characters left in the buffer from input prior to this line. Please post a complete program that shows the problem, along with the input you gave.
Try:
#include <stdio.h>
#define MAX_LEN 64
int main(void)
{ bool act = true;
char input_string[MAX_LEN]; /* character array to store the string */
int i;
printf("Enter a string:\n");
fgets(input_string,sizeof(input_string),stdin); /* read the string */
/* print the string by printing each element of the array */
for(i=0; input_string[i] != 10; i++) // \0 = 10 = new line feed
{ //the number in each digits can be only 0-9.[ASCII 48-57]
if (input_string[i] >= 48 and input_string[i] <= 57)
continue;
else //must include newline feed
{ act = false; //0
break;
}
}
if (act == false)
printf("\nTHIS IS NOT INTEGER!");
else
printf("\nTHIS IS INTEGER");
return 0;
}
[===>] First we received input using fgets.Then it's will start pulling each digits out from input(starting from digits 0) to check whether it's number 0-9 or not[ASCII 48-57],if it successful looping and non is characters -- boolean variable 'act' still remain true.Thus returning it's integer.
I have a variable int number and I need code that only allows introducing 1, 2, 3, 4 or 5 to the variable.
I have scanf and I just do an if...else to check out if the number is between 1 and 5, but the point is that I don't know what to do if I introduce a character, e.g. 'q'. I mean, I want the program to say "That's a character, not a number."
use sscanf on a buffer (or in the example below, argv the program argument) to attempt to find a digit, then a character.
The return code from sscanf is :
...the number of input items successfully matched
and assigned, which can be fewer than provided for, or even zero in the
event of an early matching failure.
Here is an example program
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(int argc, char ** argv){
int val=-1;
char other;
int code;
if (argc !=2 ) {
printf("please give an arg to program\n");
exit(0);
}
code=sscanf(argv[1],"%d",&val);
if (code == 1) {
printf( "number %d", val);
}
else {
code=sscanf(argv[1],"%c",&other);
if (code == 1) {
printf("character is %c", other);
}
else {
printf("error occured");
}
}
}
Only accept input of 1 to 5, inclusive:
#include <stdio.h>
int main() {
int rc, answer = 0;
printf("A number between 1 and 5: ");
rc = scanf("%i", &answer);
if(rc == EOF || rc == 0 || answer > 5 || answer <= 0) {
fprintf(stderr, "Only numbers between 1 and 5, please.\n");
return -99;
} else {
printf("You entered: %d\n", answer);
return 0;
}
}
Return Value
These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.
The value EOF is returned if the end of input is reached before either the first successful conversion or a matching failure occurs. EOF is also returned if a read error occurs, in which case the error indicator for the stream (see ferror(3)) is set, and errno is set indicate the error.
Basically, scanf will continue to scan until a match is made or it fails. An example program would be this:
#include <stdio.h>
int main() {
int number, result;
result = scanf("%i", &number);
if(result == EOF) {
// an error occured.
fprintf(stderr, "That's not a number");
} else {
// logic! yay!
// the result is in `number`.
}
}
Use man scanf for more information.
Try with this code:
#include <stdio.h>
int clearStdin()
{
while (getchar() != '\n');
return 1;
}
int main()
{
int integer = 0;
char character;
printf("Enter an integer not a character:\n");
do
{
printf("? ");
} while (((scanf("%d%c", &integer, &character) != 2) || (character != '\n')) && clearStdin());
printf("You entered the following integer: %d\n", integer);
return 0;
}
scanf will return the number of successful conversions and assignments, so you can check that to make sure you read at least 1 digit character:
int value;
...
if ( scanf( "%d", &value ) == 1 )
{
// read at least 1 digit
}
else
{
// first character was not whitespace or a digit
}
What's this "read at least 1 digit" nonsense? The %d conversion specifier tells scanf to skip any leading whitespace, then read and convert decimal digits until it finds a non-digit character. This means if you type a string like "12w4", scanf will convert and assign 12 to value, return 1, and leave "w4" in the input stream to foul up the next read.
That's probably not the behavior you want. You could scan for a following non-digit character, like so:
#include <ctype.h>
...
int value;
char follow;
...
if ( scanf( "%d%c", &value, &follow ) == 2 )
{
if ( !isspace( follow ))
{
fprintf( stderr, "detected non-digit character in input\n" );
}
}
else
{
// first character was not whitespace or a digit
}
You've caught that case, but your input may now be in a bad state; how do you know if the next character was part of a bad input or not? IOW, how do you know the input was 12w4 and not 12w 4?
Personally, I take a different approach. I read the input as a string, then use strtol to perform the conversion:
/**
* Assuming a 64-bit integer value, average 3.2 bits per decimal digit, we
* need to store up to 22 digits plus an optional sign character plus the 0
* terminator
*/
define INPUT_SIZE 24
...
char input[INPUT_SIZE] = {0};
long value = 0;
...
if ( fgets( input, sizeof input, stdin ))
{
char *newline = strchr( input, '\n' );
if ( !newline )
{
fprintf( stderr, "Input is too long for a 64-bit integer...rejecting\n" );
/**
* Cycle through the input stream until we see the newline character
*/
while ( fgets( input, sizeof input, stdin ) && !strchr( input, '\n' ))
;
}
else
{
char *chk; // will point to the first character that's *not*
// part of a decimal integer
long temp = strtol( input, &chk, 10 );
if ( !isspace( *chk ) && *chk != 0 )
{
fprintf( stderr, "%s is not a valid integer input\n", input );
}
else
{
value = temp;
}
}
}
This allows me to catch cases where the input value is obviously too long to store in the target data type, something from which scanf does not protect you. It also allows me to catch cases where a non-digit character is fat-fingered in the middle of the input and reject the whole input outright, something else from which scanf doesn't protect you.
Simple read 1 char and test it.
int ch;
while ((ch = fgetc(stdin)) != EOF) {
if ((ch < '1') || (ch > '5')) {
fputs(stdout, "That's a not number 1 to 5.\n");
}
else {
ch -= '0';
printf("Good: %d entered\n", ch);
}
}