C: error replacing gets() with fgets() - c

I am currently having an issue replacing gets() with fgets(). I have looked at multiple examples of doing this and it seems very straight forward however I am getting unexpected output in doing so. Using the gets() method in comments below, I get good behavior from my shell program I am writing, however when I change to the fgets() call, I get output ": no such file or directory" when giving input "ls". Like I said, with the gets() call it is working fine. code below:
int main(void) {
while(1) {
int i = 0;
printf("$shell: ");
scanf("%s", first);
/* gets(input);*/
fgets(input, sizeof(input), stdin);
//...parse input into tokens for exec system call...
execvp(first, args);
}
return 0;
}

Unlike gets, fgets will read the newline and store it in the string.
From the man page:
fgets() reads in at most one less than size characters from stream
and stores them into the buffer pointed to by s. Reading stops
after an EOF or a newline. If a newline is read, it is stored into
the buffer. A '\0' is stored after the last character in the
buffer.
You can remove the newline (if it is present) by replacing it will a null byte:
fgets(input, sizeof(input), stdin);
if (input[strlen(input)-1] == '\n') input[strlen(input)-1] = '\0';

Related

getline and fgets failed to get input on first attempt

void someFunction(){
char *buffer;
size_t bufsize = 32;
int bytes_read;
for (;;) {
buffer = (char *) malloc(bufsize * sizeof(char));
if (buffer == NULL) {
perror("Unable to allocate buffer");
exit(1);
}
FILE *ptr;
ptr = fopen("sample.txt", "a");
printf("Enter Stuff to write down:\n");
//getline(&buffer,&bufsize,stdin);
//fgets(buffer, 30, stdin);
//scanf("%[^\n]%*c", buffer);
//scanf("%s", buffer);
if (buffer[0] == '0') {
break;
}
WriteWithFprintf(ptr, buffer);
free(buffer);
fclose(ptr);
}
}
The problem is: if I use
getline(&buffer,&bufsize,stdin);
or
fgets(buffer, 30, stdin);
then it escapes the first like so:
Enter Stuff to write down:
Enter Stuff to write down:
0
If I use:
scanf("%[^\n]%*c", buffer);
then I get an infinite loop.
It does work with:
scanf("%s", buffer);
but I want input with space so this is not an option for me.
All of the behaviors described for different variations on your code are consistent with the next character available to be read from stdin being a newline, presumably from a preceding line of input.
In that case,
the getline() and fgets() alternatives will read the newline (and any preceding characters) as a line, and then loop to read the line you actually want on the second pass.
the first scanf() variation will read nothing on account of a matching failure for the %[^\n] field (leading whitespace is not skipped for %[ directives). Not having matched anything to that, there will be no attempt to match anything to the %*c.
the second scanf() alternative will work as you describe, because scanf will automatically consume leading whitespace when processing a %s directive, including any newline.
There is a variety of things you could do, depending on exactly how want to handle input. Here is one:
int c = fgetc(stdin);
if (c == EOF) {
// handle eof ...
} else if (c != '\n') {
ungetc(c, stdin);
}
// your choice for reading the wanted data ...
That will consume up to one leading newline from stdin to get it out of your way.
getline and fgets failed to get input on first attempt
Neither failed. Both simply read a '\n' and immediately returned. This '\n' was left-over from a previous input function like scanf("%s", ...), that did not consume the entire line. getline() is not part of the standard C library.
scanf("%[^\n]%*c", buffer); fails to read anything when the first available character is '\n'.
scanf("%s", buffer); consumes all optional leading white space like '\n'. This may appear to work for OP.
scanf("%[^\n]%*c", buffer); and scanf("%s", buffer); are both poor code as they do not have a width limit, risking buffer overflow.
I recommend for a learner to not use scanf() at all and perform all input with fgets(), including the part of code not posted.

fgets() goes newline when storing string but gets() no issue about newline

fseek(fPtr, 0, SEEK_END);
fflush(stdin);
printf("\n\t\t\t ENTERID : ");
fgets(user.id, ID_SIZE, stdin);
fflush(stdin);
printf("tENTER FIRST NAME: ");
fgets(user.fname, MAX_FNAME_SIZE, stdin); //automatic added newline
printf("ENTER LAST NAME: ");
fgets(user.lname, MAX_LNAME_SIZE, stdin); //automatic added newline
I was using fgets() for reading the input of the string and store into the text file using the fwrite.
But why does fgets() automatically enter a newline for each input of string.
"...but gets() no issue about newline"
Note, that although your observation about gets() being preferable in this case over fgets() for handling newline, the unfavorable behaviors that come with gets() make it dangerous to use, with the result that "it was officially removed by the 2011 standard." (credit) Even without the \n mitigations mentioned below, fgets() is highly preferred over gets().
"fgets() goes newline when storing string..." and "...why does fgets() automatically enter a newline for each input of string"
fgets() does not enter the newline upon reading the line, rather if one exists, the newline is picked up as part of the line when fgets() called. For example in this case, when using stdin as the input method, the user clicks the <return> to finish inputting text. Upon hitting the <return> key, a \n is entered just like any other character, and becomes the last character entered. When the line is read using fgets(), if the \n is seen before any of its other stop reading criteria, fgets() stops reading, and stores all characters, including \n, terminates line with \0 and stores into the buffer. (If sizeof(buffer) - 1 or EOF is seen first, fgets() will never see the newline.)
To easily eliminate the \n, (or other typical unwanted line endings), use the following single line statements after each of your calls to fgets():
fgets(user.id, ID_SIZE, stdin);
user.id[strcspn(user.id, "\n")] = 0;
//fflush(stdin);//UB, should not be called
...
fgets(user.fname, MAX_FNAME_SIZE, stdin);
user.fname[strcspn(user.fname, "\n")] = 0;
...
fgets(user.lname, MAX_LNAME_SIZE, stdin);
user.lname[strcspn(user.lname, "\n")] = 0;
...
This technique works for truncating any string by searching for the unwanted char, whether it be "\n", "\n\r", "\r\n", etc. When using more than one search character, eg "\r\n", it searches until it reaches either the \r or the \n and terminates at that position.
"This [method] handles the rare buffer than begins with '\0', something that causes grief for the buffer[strlen(buffer) - 1] = '\0'; [method]." (#Chux - comment section of link below.)
Credit here
Quoting from the man page,
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte ('\0') is stored after the last character in the buffer.
So, after providing the input from the terminal, you press the ENTER key which adds a newline to the input buffer. That same newline is scanned and stored in the destination provided to fgets().
If you want to remove the trailing newline, use the solution mentioned in the answer: Removing trailing newline character from fgets() input
That said, read:
Why is the gets function so dangerous that it should not be used?
How is the working of fflush(stdin) changing the output in below code?

How to read input in C

I'm trying to read a line with scanf("%[^\n]"); right before it I'm reading an integer with "%d", was told to me that scanf doesn't erase the '\n' after reading, so I have to call fflush() to avoid it, but even doing that I still have the same problems, so here is my code:
scanf("%d", &n);
fflush(stdin);
lines = (char**)malloc(sizeof(char*)*n);
for(i = 0; i < n; i++){
lines[i] = (char*)malloc(sizeof(char)*1001);
}
for(i = 0;i < n;i++){
scanf("%[^\n]", linhes[i]);
}
I read an integer and then the scanf doesn't wait, it starts reading the input — doesn't matter what the integer value is, whether 5 or 10, the scanf reads all the strings to empty. Already tried with fgets and the result is almost the same, except that it reads some of the strings and skips others.
Let us look at this step by step:
"... read a line with scanf("%[^\n]");".
scanf("%[^\n]", buf) does not read a line. It almost does - sometimes. "%[^\n]" directs scanf() to read any number of non-'\n' char until one is encountered (that '\n' is then put back into stdin) or EOF occurs.
This approach has some problems:
If the first char is '\n', scanf() puts it back into stdin without changing buf in anyway! buf is left as is - perhaps uninitialized. scanf() then returns 0.
If at least one non-'\n' is read, it is saved into buf and more char until a '\n' occurs. A '\0' is appended to buf and the '\n' is put back into stdin and scanf() returns 1. This unlimited-ness can easily overfill buf. If no char was saved and EOF or input error occurs, scanf() returns EOF.
Always check the return value of scanf()/fgets(), etc. functions. If your code does not check it, the state of buf is unknown.
In any case, a '\n' is still usually left in stdin, thus the line was not fully read. This '\n' often is an issue for the next input function.
... scanf doesn't erase the '\n' after reading
Another common misconception. scanf() reads a '\n', or not, depending on the supplied format. Some formats consume '\n', others do not.
... call fflush() to avoid it
fflush(stdin) is well defined in some compilers but is not in the C standard. The usual problem is code wants to eliminate any remaining data in stdin. A common alternative, when the end of the line had not yet occurred, is to read and dispose until '\n' is found:
int ch; // Use int
while ((ch = fgetc(stdin)) != '\n' && ch != EOF);
I still have the same problems
The best solution, IMO, is to read a line of user input and then scan it.
char buf[sizeof lines[i]];
if (fgets(buf, sizeof buf, stdin) == NULL) return NoMoreInput();
// If desired, remove a _potential_ trailing \n
buf[strcspn(buf, "\n")] = 0;
strcpy(lines[i], buf);
I recommend that a buffer should be about 2x the size of expected input for typical code. Robust code, not this snippet, would detect if more of the line needs to be read. IMO, such excessively long lines are more often a sign of hackers and not legitimate use.
BLUEPIXY in the comment answered my question:
try "%[^\n]" change to " %[^\n]"

Why is gets() not consuming a full line of input?

I'm trying to use gets() to get a string from the user, but the program seems to be passing right over gets(). There is no pause for the user to give input. Why is gets() not doing anything?
char name[13];
printf("Profile name: ");
gets(name);
printf("\n%s", name);
Call getchar() before you call gets() or fgets(). Since gets() or fgets() is getting skipped due to an already present '\n' from previous inputs in stdin, calling getchar() would lead to itself getting skipped instead of gets() or fgets() or any other similar function. But remember its more of a hack and not a standard solution (I think so), and also use of gets() is forbidden.
printf("\nEnter a String: ");
getchar();
//fgets(inputString, 100, stdin);
gets(inputString);
printf("\n%s", inputString);
It's because gets() it's so incredibly dangerous to use, that some C libraries have removed it completely and replaced it with a version that does nothing.
Use fgets() instead.
Use fflush(stdin); before gets()
You get lot of troubles using gets()
Instead go for fgets()
fgets(name,13,stdin);
See this SO question Why is the gets function so dangerous that it should not be used?
The reason why fgets() does not work, may be you are not handling the newline left behind by scanf in your previous statements.
You can modify your scanf format string to take it into account:
scanf("%d *[^\n]", &N);
*[^\n] says to ignore everything after your integer input that isn't a newline, but don't do anything with the newline (skip it).
When you use scanf("%d",&num) you hit 13 and enter and 13 is stored in num and the newline character is still in the input buffer when you read fgets from stdin it treats \n as the data you have entered and the fgets() statement is skipped
You cannot flush input buffer however you can do this fseek(stdin,0,SEEK_END); add this before your every fgets statement
Take a look at gets() reference
Get string from stdin
Reads characters from the standard input (stdin) and stores them as a C string into str until a newline character or the end-of-file is reached.
The newline character, if found, is not copied into str.
A terminating null character is automatically appended after the characters copied to str.
Notice that gets is quite different from fgets: not only gets uses stdin as source, but it does not include the ending newline character in the resulting string and does not allow to specify a maximum size for str (which can lead to buffer overflows).
So, basically gets() is not only unsafe (can lead to buffer overflows) but also reads what's in the input buffer.
I recommend you to use fgets(), but if you want a quick (and lazy and stupid) solution, just flush the input buffer:
char name[13];
printf("Profile name: ");
fflush(stdin);
gets(name);
printf("\n%s", name);
Add this below function to your code and enjoy it.
string GetString()
{
char ch;
string Line="";
while(1)
{
ch=getchar();
if(ch=='\n')
break;
else
Line+=ch;
}
return Line;
}
This function can effect all Spaces and Backspaces too!!!
You might need to discard the rest of the line and move on to the beginning of the next one. You can do:
int c; while((c=getchar()) != '\n' && c != EOF);
This discards the rest of the current line (which might have been partially read with scanf(), etc.) and moves on to the next one.
Although fflush(stdin) might also discard the input, it is non-standard behaviour and discouraged.
You should never ever use gets(myString), as it has been completely removed from the C standard due to how unsafe it is. It doesn't enforce the length of the string, so you can go past the end of the buffer and overwrite other values in RAM, causing a buffer overflow, which can be a security vulnerability. Use fgets(myString, myStringSize, stdin); instead. To get rid of the newline in the resulting string, use myString[strcspn(myString, "\n")] = 0;.

strstr not functioning

Why does this particular piece of code return false on the strstr() if I input "test"?
char input[100];
int main()
{
fgets(input, 100, stdin);
printf("%s", input);
if(strstr("test message", input))
{
printf("strstr true");
}
}
I thought strstr searched the first param for instances of the second param? It works when I replace input with some text or just assign it something directly, but it seems to not work with fgets.
It's because fgets stores the newline character so when strstr does a comparison it fails.
From the man page:
fgets() reads in at most one less than size characters from stream
and stores them into the buffer pointed to by s. Reading stops after
an EOF or a newline. If a newline is read, it is stored
into the buffer. A '\0' is stored after the last character in the buffer.
Add input[strlen(input) - 1] = '\0'; after the fgets. fgets reads in the newline char ('\n'). There is no '\n' in "test message" so input will never be contained within it.
You should really check to see if the newline is at the end of the buffer after calling fgets to know if the whole line was able to actually fit into it, and also to obviously remove it.

Resources