scanf unknown number of integers, how to end loop? - c

In class I need to use scanf to get integers to work with. Problem is I do not know to end the while loop. I wait for '\n' in the code, but it is passing all tests. The program has to complete for grading.
How to make code work when input includes several '\n' in input and spacebars at the end of input.
All numbers are given with spacebar between.
# include <stdio.h>
int main()
{
int numbers;
char ch;
int stop = 0;
while(scanf("%d%c", &numbers, &ch))
{
if((ch == '\n') stop++;
#my_code
if (stop == 1) break;
}

while(scanf("%d%c", &numbers, &ch)) { if((ch == '\n') .... has a couple of problems.
If the line of input has only white-space like "\n" or " \n", scanf() does not return until non-white-space is entered as all leading white-spaces are consumed by "%d".
If space occurs after the int, the "\n" is not detected as in "123 \n".
Non-white-space after the int is discarded as in "123-456\n" or "123x456\n".
how to end loop?
Look for the '\n'. Do not let "%d" quietly consume it.
Usually using fgets() to read a line affords the more robust code, yet sticking with scanf() the goal is to examine leading white-space for the '\n'
#include <ctype.h>
#include <stdio.h>
// Get one `int`, as able from a partial line.
// Return status:
// 1: Success.
// 0: Unexpected non-numeric character encountered. It remains unread.
// EOF: end of file or input error occurred.
// '\n': End of line.
// Note: no guards against overflow.
int get_int(int *dest) {
int ch;
while (isspace((ch = fgetc(stdin)))) {
if (ch == '\n') return '\n';
}
if (ch == EOF) return EOF;
ungetc(ch, stdin);
int scan_count = scanf("%d", dest);
return scan_count;
}
Test code
int main(void) {
unsigned int_count = 0;
int scan_count;
int value;
while ((scan_count = get_int(&value)) == 1) {
printf("%u: %d\n", ++int_count, value);
}
switch (scan_count) {
case '\n': printf("Normal end of line.\n"); break;
case EOF: printf("Normal EOF.\n"); break;
case 0: printf("Offending character code %d encountered.\n", fgetc(stdin)); break;
}
}

Related

Is there an elegant way to handle the '\n' that gets read by input functions (getchar(), fgets(), scanf()) in C?

I am trying a simple exercise from K&R to append string2 at the end of string1 using pointers. In case of overflow i.e. buffer of string1 can't contain all of string2 I want to prompt the user to re-enter string2 or exit.
I have written the following code:
#include<stdio.h>
#include<string.h>
#define MAXLINE 1000
int get_input(char *s);
int str_cat(char *s, char *t);
void main()
{
char input1[MAXLINE], input2[MAXLINE], c;
get_input(input1);
check:
get_input(input2);
if((strlen(input1) + strlen(input2) + 2) <= MAXLINE)
{
str_cat(input1, input2);
printf("%s\n", input1);
}
else
{
input2[0] = '\0';
printf("String overflow\n Press: \n 1: Re-enter string. \n 2: Exit.\n");
scanf(" %d", &c);
if(c == 1){
input2[0] = '\0';
get_input(input2);
goto check;
}
}
}
int get_input(char *arr)
{
int c;
printf("Enter the string: \n");
while(fgets(arr, MAXLINE, stdin))
{
break;
}
}
int str_cat(char *s, char *t)
{
while(*s != '\0')
{
s++;
}
while((*s++ = *t++) != '\0')
{
;
}
*s = '\0';
}
Initially, I was using the standard getchar() function mentioned in the book to read the input in get_input() which looked like this:
int get_input(char *arr)
{
int c;
printf("Enter the string: \n");
while((c = getchar()) != '\n' && c != EOF)
{
*arr++ = c;
}
*arr = '\0';
}
I am new and I read this and understood my mistake. I understand that one isn't supposed to use different input functions to read stdin and the '\n' is left in the input stream which is picked by the getchar() causing my condition to fail.
So, I decided to use fgets() to read the input and modified the scanf("%d", &c) as mentioned in the thread with scanf(" %d", c). This does work (kinda) but gives rise to behaviors that I do not want.
So, I have a few questions:
What's a better way to fgets() from reading the input on encountering '\n' than the one I have used?
while(fgets(arr, MAXLINE, stdin))
{
break;
}
fgets() stops reading the line and stores it as an input once it either encounters a '\n' or EOF. But, it ends up storing the '\n' at the end of the string. Is there a way to prevent this or do I have to over-write the '\n' manually?
Even though I used the modified version of scanf(" %d", &c), my output looks like
this: (https://imgur.com/a/RaC2Kc6). Despite that I get Enter the string: twice when prompted to re-enter the second string in case of an overflow situation. Is the modified scanf() messing with my input? And how do I correct it?
In general, do not mix fgets with scanf. Although it may be a bit bloaty, you will avoid many problems by being consistent with reading input with fgets and then parse it with sscanf. (Note the extra s)
A good way to remove the newline is buffer[strcspn(buffer, "\n")] = 0
Example:
// Read line and handle error if it occurs
if(!fgets(buffer, buffersize, stdin)) {
// Handle error
}
// Remove newline (if you want, not necessarily something you need)
buffer[strcspn(buffer, "\n")] = 0;
// Parse and handle error
int val;
if(sscanf(buffer, "%d", &val) != 1) {
// Handle error
}
// Now you can use the variable val
There is one thing here that might be dangerous in certain situations, and that is if buffer is not big enough to hold a complete line. fgets will not read more than buffersize characters. If the line is longer, the remaining part will be left in stdin.

How to take input until enter is pressed twice?

I want to break this loop when the user press enters twice. Meaning, if the user does not enter a character the second time, but only presses enter again, the loop must break.
char ch;
while(1) {
scanf("%c",&ch);
if(ch=='') { // I don't know what needs to be in this condition
break;
}
}
It is not possible to detect keypresses directly in C, as the standard I/O functions are meant for use in a terminal, instead of responding to the keyboard directly. Instead, you may use a library such as ncurses.
However, sticking to plain C, we can detect newline characters. If we keep track of the last two read characters, we can achieve similar behavior which may be good enough for your use-case:
#include <stdio.h>
int main(void)
{
int currentChar;
int previousChar = '\0';
while ((currentChar = getchar()) != EOF)
{
if (previousChar == '\n' && currentChar == '\n')
{
printf("Two newlines. Exit.\n");
break;
}
if (currentChar != '\n')
printf("Current char: %c\n", currentChar);
previousChar = currentChar;
}
}
Edit: It appears that the goal is not so much to detect two enters, but to have the user:
enter a value followed by a return, or
enter return without entering a value, after which the program should exit.
A more general solution, which can also e.g. read integers, can be constructed as follows:
#include <stdio.h>
#define BUFFER_SIZE 64U
int main(void)
{
char lineBuffer[BUFFER_SIZE];
while (fgets(lineBuffer, BUFFER_SIZE, stdin) != NULL)
{
if (lineBuffer[0] == '\n')
{
printf("Exit.\n");
break;
}
int n;
if (sscanf(lineBuffer, "%d", &n) == 1)
printf("Read integer: %d\n", n);
else
printf("Did not read an integer\n");
}
}
Note that there is now a maximum line length. This is OK for reading a single integer, but may not work for parsing longer input.
Credits: chux - Reinstate Monica for suggesting the use of int types and checking for EOF in the first code snippet.
You can store the previous character and compare it with the current character and enter, like this:
char ch = 'a', prevch = '\n';
while(1){
scanf("%c",&ch);
if((ch=='\n') && (ch == prevch)){// don't know what needs to be in this condition
break;
}
prevch = c;
}
Note that the previous character by default is enter, because we want the program to stop if the user hits enter at the very start as well.
Working like charm now
char ch[10];
while(1){
fgets(ch, sizeof ch, stdin);
if(ch[0]=='\n'){
break;
}
}

By default, reading which character in the input buffer makes scanf() stop reading a string?

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;
}

Replace the usage of gets with getchar

I currently have a homework assignment and I used gets.
The professor said I should be using getchar instead.
What is the difference?
How would I change my code to use getchar? I can't seem to get it right.
code:
#include <stdio.h>
#include <string.h>
#include <strings.h>
#define STORAGE 255
int main() {
int c;
char s[STORAGE];
for(;;) {
(void) printf("n=%d, s=[%s]\n", c = getword(s), s);
if (c == -1) break;
}
}
int getword(char *w) {
char str[255];
int i = 0;
int charCount = 0;
printf("enter your sentence:\n"); //user input
gets(str);
for(i = 0; str[i] != '\0' && str[i] !=EOF; i++){
if(str[i] != ' '){
charCount++;
} else {
str[i] = '\0'; //Terminate str
i = -1; //idk what this is even doing?
break; //Break out of the for-loop
}
}
printf("your string: '%s' contains %d of letters\n", str, charCount); //output
strcpy(w, str);
// return charCount;
return strlen(w); //not sure what i should be returning.... they both work
}
gets() was supposed to get a string from the input and store it into the supplied argument. However, due to lack of preliminary validation on the input length, it is vulnerable to buffer overflow.
A better choice is fgets().
However, coming to the usage of getchar() part, it reads one char at a time. So basically, you have to keep reading from the standard input one by one, using a loop, until you reach a newline (or EOF) which marks the end of expected input.
As you read a character (with optional validation), you can keep on storing them in str so that, when the input loop ends, you have the input string ready in str.
Don't forget to null terminate str, just in case.

fgets and readRestOfLine errors

I got errors while creating a menu using fgets and readrestofline function. I don't know where is the error coming from. Am I missing something? After compiling, errors shows at "fgets", "readrestofline" and "stdin".
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int printMenu(void)
{
int option;
char input[3];
while((option != 3)||(option < 4)||(option > 0))
{
printf("Welcome\n");
printf("---------------------\n");
printf("1.Play \n2.Display Scores\n3.Quit\n");
printf("Please enter your choice: ");
fgets(input, 3, stdin);
if (input[strlen(input) - 1] != '\n')
{
printf("Input was too long.\n");
readRestOfLine();
}
else
{
input[strlen(input) - 1] = '\0';
}
switch (option)
{
case 1:
printf("Loading ...\n");
break;
case 2:
printf("Loading ...\n");
break;
case 3:
printf("Quitting...\n");
exit(0);
break;
default:
printf("Invalid ! Please choose again.\n");
break;
}
}
}
void readRestOfLine()
{
int c;
/*read until the end of the line or end-of-file*/
while ((c = fgets(stdin)) != '\n' && c != EOF);
/*clear the error and end-of-file flags*/
clearerr(stdin);
}
errors while creating a menu using fgets...
Regarding your code line:
while ((c = fgets(stdin)) != '\n' && c != EOF);
fgets, prototype is:
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. The newline character is retained. An ASCII NUL byte is appended to the end of the string. If successful, the function returns a pointer to lineBuffer.
you have only provided 1 of the 3 necessary arguments.
Example usage:
char buf[80];//line buffer with space for 80 char
int c;
while(fgets(buf, 80, stdin))
{
//do something with buf
}
Also, instead of using the line(s): (undefined behavior)
if (input[strlen(input) - 1] != '\n') //used twice in your code example
Consider testing the contents of the string(s) like this:
if(strstr(input, "\n"))//change the second argument to search for other values
{
//do something
}
Note that your first usage of fgets in the posted code is syntactically correct.

Resources