fgets() does not return NULL on empty string - c++

I recently tried to use fgets() instead of scanf() to read a string for code security reasons. I used a simple function that I found here to check for errors (no input and too long input). The problem is that whenever i press "ENTER" without actually writing anything, fgets() doesn't return NULL and my program is not able to show the NO_INPUT error.
Here's main.cpp:
#include <stdlib.h>
#include <stdio.h>
#include "utilities.h"
int main() {
int rc;
char str[20];
rc = getLine("Enter string: ", str, sizeof(str));
if(rc == NO_INPUT) {
printf("[ERROR] No input\n\n");
} else if(rc == TOO_LONG) {
printf("[ERROR] Input is too long\n\n");
} else {
printf("This is your input: \"%s\"\n\n", str);
}
system("pause");
}
Here's utilities.h:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
int getLine(const char *msg, char *buff, size_t len) {
if(msg != NULL) {
printf("%s", msg);
fflush(stdout);
}
if(fgets(buff, len, stdin) == NULL /*[+]*/ || strcmp(buff, "\n") == 0) {
//[-] fflush(stdin);
return NO_INPUT;
} else if(buff[strlen(buff)-1] != '\n') {
//[-] fflush(stdin);
return TOO_LONG;
} else {
buff[strlen(buff)-1] = '\0';
//[-] fflush(stdin);
return OK;
}
}
And here's the output:
Enter string:
This is your input: ""
Press any key to continue . . .
I solved my problem by replacing the first if statement in utilities.h with if(fgets(buff, len, stdin) == NULL || strcmp(buff, "\n") == 0). Doing this, my program will check for input errors OR for empty strings and return the NO_INPUT flag.
Thanks to everybody who commented and #Peter for answering. I added the aforementioned if statement in utilities.h and removed every fflush(stdin) occurrence. The code now looks as above.

Your problem that you "fixed" is believing that a end of line should be treated as end of input.
NULL is an indication from fgets() that it encountered an error or the end of input when reading from the file (or stream). A blank line is neither an error nor a marker of end of input. A human (typing on a keyboard) might choose to interpret a newline as end of input, but a computer program does not - after all, there is nothing stopping a user entering more than one line of input.
Practically, fgets() reads a line and indicates the end of that line with a '\n' character. Let's say, we have a file containing
ABC
DE
FGHIJ
(blank lines interspersed in three lines of text, followed by end of the file).
Let's also that buffer is an array of five char, and that we read that file using consecutive statements of the form fgets(buffer, 5, file).
So what will fgets(buffer, 5, file) do on each call. Well, with 1 representing the first call, 2 representing the second call, etc we will see results of
"ABC\n" stored into buffer;
"\n" stored into buffer; (first blank line)
"DE\n" stored into buffer;
"\n" stored into buffer; (second blank line)
"FGHI" stored into buffer;
"J\n" stored into buffer; and
fgets() returns NULL, and nothing is stored into buffer.
The first six calls will all return &buffer[0] - not NULL - since no error is encountered reading from the file. Even though there are two blank lines in the input. The last line, which is longer than the buffer (with the '\n' counted) is read in two parts.
Incidentally, your code is using fflush(stdin). Unfortunately, fflush() only has defined behaviour on OUTPUT streams or files. Using it on stdin (or any input stream) gives undefined behaviour. If it is actually discarding input (which it does with some implementations of the C standard library), you are getting lucky - there are real-world compilers where the resultant behaviour does not discard input.

Related

Why does scanf_s() isn't working second time I am calling it in order to verify if user provided correct input?

I am writing an application with a menu and I am asking the user to provide an integer representing an option
from the menu
1. Option 1
2. Option 2
3. Option 3
...
This option is stored in a variable called
option
I want to avoid wrong input such as "12a", "1a2", "anyString" and I've read that this can be achieved by storing return value of scanf_s() function.
So I stored it in a variable called
ret
and now I want that every time user provides wrong input to prompt them to enter a new value.
So I wrote something like this:
int ret = scanf_s("%d", &option);
while (!ret)
{
cout << "Provide an integer, not a string! :)\n";
ret = scanf_s("%d", &option);
}
The problem is when it enters the while it is not allowing user to enter a new value and hence the value of ret never changes, making it run endlessly.
How can I achieve what I am looking for?
When scanf_s fails to convert an integer, the offending input stays in the input stream. Calling scanf_s("%d", &option) again will produce the same result until some characters are removed from the input stream. Note also that using scanf_s or scanf directly from stdin is error prone: the newline entered by the user stays in the input stream and will be read by a subsequent call to getchar() or fgets(), potentially causing unexpected behavior.
To avoid these pitfalls, it is recommended to read one line at a time with fgets() and convert it with sscanf() this way:
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdio.h>
int main() {
char buf[80];
int option;
char cc;
for (;;) {
print_menu(); // output the menu options
if (!fgets(buf, sizeof buf, stdin)) {
/* end of file reached: break from the loop */
break;
}
/* parse exactly one integer with optional leading and trailing whitespace */
if (sscanf(buf, "%d %c", &option, &cc) != 1) {
printf("invalid input: %s", buf);
printf("enter one integer exactly\n");
continue;
}
printf("option is %d\n", option);
// handle option
}
return 0;
}

Printf display only one word

I want to display more than one word using printf, Do I should change first parameter in pritnf?
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
int value;
printf("How many:"); scanf("%d", &value);
char* arr1 = new char[value];
scanf("%s[^\n]", arr1);
printf("%s", arr1);
delete [] arr1;
return 0;
}
As already pointed out in the comments section, the following line is wrong:
scanf("%s[^\n]", arr1);
The %s and %[^\n] are distinct conversion format specifiers. You seem to be attempting to use a hybrid of both. If you want to read a whole line of input, you should use the second one.
However, even if you fix this line, your program will not work, for the following reason:
The statement
scanf("%d", &value);
will read a number from standard input, but will only extract the number itself from the input stream. The newline character after the input will not be extracted.
Therefore, when you later call
scanf("%[^\n]", arr1);
it will extract everything that remained from the previous scanf function call up to the newline character. This will result in no characters being extracted if the newline character immediately follows the number (which is normally the case).
Example of program's behavior:
How many:20ExtraInput
ExtraInput
As you can see, everything after the number up to the newline character is being extracted in the second scanf function call (which is then printed). However, this is not what you want. You want to extract everything that comes after the newline character instead.
In order to fix this, you must discard everything up to and including the newline character beforehand. This must be done between the two scanf function calls.
#include <stdio.h>
int main()
{
int value;
int c;
printf("How many:"); scanf("%d", &value);
char* arr1 = new char[value];
//discard remainder of line, including newline character
do
{
c = getchar();
} while ( c != EOF && c != '\n' );
scanf("%[^\n]", arr1);
printf("%s", arr1);
delete [] arr1;
return 0;
}
The program now has the following behavior:
How many:20ExtraInput
This is a test.
This is a test.
As you can see, the program now discards ExtraInput and it correctly echoes the line This is a test.

(C/C++) EOF in windows7

#include <cstdio>
void fcopy(FILE* from, FILE* to);
int main()
{
fcopy(stdin, stdout);
return 0;
}
void fcopy(FILE* from, FILE* to)
{
int c;
while ((c = getc(from)) != EOF) {
putc(c, to);
}
}
When I run this program, some unexpected behaviour happens with ^Z (Ctrl+z), which I would use to indicate EOF.
My input of "hello\n" executes the while loop in 'fcopy' to print the same.
"^Z\n" ends the program.
But if I input "blahblah^Zasdfasdf\n", while I expected the program to print "blahblah" and terminate, it prints "blahblah→" with a little arrow and waits for my input. Whatever I write down here will be copied down exactly the same; it seems to re-execute the loop while cutting away anything written after '^Z'.
in: hello
out: hello
in: hello^Z
out/in: hello→?? // "??" is my input
out: ??
in: ^Z
termination
Could anyone please clarify why the program works that way?
Thanks for any help, in advance.
This is because the Windows terminal program, which is what is reading your keyboard input and passing it on to your program, is handling Ctrl+Z like that. It only means "signal end of input" when pressed at the start of a line.
Note that EOF in C does not represent an actual ("physical") character, it's out-of-band signalling to indicate that "there was no character to be read, since the file has ended".
In general I would expect you that you should wait for ^Z and not EOF. ^Z is 0x1A ASCII character (http://en.wikipedia.org/wiki/Substitute_character). Also, you should definitely also check for EOF since file can end without ^Z
It seems that for some reason, console applications interpret ^Z as EOF when line is empty (I'm not sure why though - This could be legitimate behavior or simply a bug as suggested in https://connect.microsoft.com/VisualStudio/feedback/details/798951/c-getc-breaks-when-encountering-character-26-or-1a-hex-ascii-sub)
But the following code fixes it:
#include <cstdio>
#define CTRL_Z_SUB 0x1A // CTRL_Z substitue ASCII key
void fcopy(FILE* from, FILE* to);
int main()
{
fcopy(stdin, stdout);
return 0;
}
void fcopy(FILE* from, FILE* to)
{
int c = getc(from);
while (c != EOF && c != CTRL_Z_SUB) {
putc(c, to);
c = getc(from);
}
}

Find a string in a file C++

I am trying to parse a file in C++. My file contents are as follows:
//Comments should be ignored
FileVersion,1;
Count,5;
C:\Test\Files\Test_1.txt 0,16777216,16777552,0,0,1,0,1,1,1;
FileVersion is the first line I need to read information. All the previous lines are just comments which begin with a '//'. How do I set my cursor to line containing FileVersion? Becuase I am using fscanf to read the information from the file.
if ( 1 != fscanf( f, "FileVersion,%d;\n", &lFileVersion ))
{
//Successfully read the file version.
}
I like to write parsers (assuming "line-based") by reading a line at a time, and then using sscanf strncmp and strcmp (or C++'s std::stringstream and std::string::substr) to check for various content.
In your example, something like:
enum Sates
{
Version = 1,
Count = 2,
...
} state = Version;
char buffer[MAXLEN];
while(fgets(buffer, MAXLEN, f) != NULL)
{
if (0 == strncmp("//", buffer, 2))
{
// Comment. Skip this line.
continue;
}
switch (state)
{
case Version:
if (0 == strncmp("FileVersion,", buffer, 12))
{
if (1 == sscanf(buffer, "FileVersion,%d;", &version))
{
state = Count;
break;
}
Error("Expected file version number...");
}
break;
...
}
}
There are of course oodles of other ways to do this.
Since this is tagged C++, I will give you a C++ solution.
You can use a single call to f.ignore() to discard the first line of the stream:
f.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Technically this skips everything up and including the newline at the end of the first line, so the stream position will be just before the newline on the second line. Formatted I/O discards leading whitespace so this will be no issue.
The above requires the use of C++ file streams since this is C++, and the use of the formatted operators operator>>() and operator<<() to perform input and output.
Not a particular C++ solution, but:
read a line with fgets (oh okay, if you want, you can substitute a C++ function for that);
if it starts with your 'comment' designator, skip to end of loop
if the line is empty (i.e., it contains only a hard return; or, possibly, check for zero or more whitespace characters and then an end-of-line), skip to end of loop
at end of loop: if you got something else, use sscanf on that string.

getline seems to not working correctly

Please tell me what am I doing wrong here. What I want to do is this:
1.Having txt file with four numbers and each of this numbers has 15 digits:
std::ifstream file("numbers.txt",std::ios::binary);
I'm trying to read those numbers into my array:
char num[4][15];
And what I'm thinking I'm doing is: for as long as you don't reach end of files write every line (max 15 chars, ending at '\n') into num[lines]. But this somewhat doesn't work. Firstly it reads correctly only first number, rest is just "" (empty string) and secondly file.eof() doesn't seems to work correctly either. In txt file which I'm presenting below this code I reached lines equal 156. What's going on?
for (unsigned lines = 0; !file.eof(); ++lines)
{
file.getline(num[lines],15,'\n');
}
So the whole "routine" looks like this:
int main()
{
std::ifstream file("numbers.txt",std::ios::binary);
char numbers[4][15];
for (unsigned lines = 0; !file.eof(); ++lines)
{
file.getline(numbers[lines],15,'\n');// sizeof(numbers[0])
}
}
This is contents of my txt file:
111111111111111
222222222222222
333333333333333
444444444444444
P.S.
I'm using VS2010 sp1
Do not use the eof() function! The canonical way to read lines is:
while( getline( cin, line ) ) {
// do something with line
}
file.getline() extracts 14 characters, filling in num[0][0] .. num[0][13]. Then it stores a '\0' in num[0][14] and sets the failbit on file because that's what it does when the buffer is full but terminating character not reached.
Further attempts to call file.getline() do nothing because failbit is set.
Tests for !file.eof() return true because the eofbit is not set.
Edit: to give a working example, best is to use strings, of course, but to fill in your char array, you could do this:
#include <iostream>
#include <fstream>
int main()
{
std::ifstream file("numbers.txt"); // not binary!
char numbers[4][16]={}; // 16 to fit 15 chars and the '\0'
for (unsigned lines = 0;
lines < 4 && file.getline(numbers[lines], 16);
++lines)
{
std::cout << "numbers[" << lines << "] = " << numbers[lines] << '\n';
}
}
tested on Visual Studio 2010 SP1
According to ifstream doc, reading stops either after n-1 characters are read or delim sign is found : first read would take then only 14 bytes.
It reads bytes : '1' (the character) is 0x41 : your buffer would be filled with 0x41 instead of 1 as you seem to expect, last character will be 0 (end of c-string)
Side note, your code doesn't check that lines doesn't go beyond your array.
Using getline supposes you're expecting text and you open the file in binary mode : seems wrong to me.
It looks like the '\n' in the end of the first like is not being considered, and remaining in the buffer. So in the next getline() it gets read.
Try adding a file.get() after each getline().
If one file.get() does not work, try two, because under the Windows default file encoding the line ends with '\n\r\' (or '\r\n', I never know :)
Change it to the following:
#include <cstring>
int main()
{
//no need to use std::ios_base::binary since it's ASCII data
std::ifstream file("numbers.txt");
//allocate one more position in array for the NULL terminator
char numbers[4][16];
//you only have 4 lines, so don't use EOF since that will cause an extra read
//which will then cause and extra loop, causing undefined behavior
for (unsigned lines = 0; lines < 4; ++lines)
{
//copy into your buffer that also includes space for a terminating null
//placing in if-statement checks for the failbit of ifstream
if (!file.getline(numbers[lines], 16,'\n'))
{
//make sure to place a terminating NULL in empty string
//since the read failed
numbers[lines][0] = '\0';
}
}
}