Using fscanf I want to process text that contains two floats, an arbitrary amount of whitespace before, between, or after the floats (including newlines/returns), and a newline character at the very end. If there are more/fewer than two numbers, then I want to detect that and report an error.
This seems to work for handling the whitespace, but it won't detect if there's more than two floats present:
fscanf(f, "%f %f", &sx, &sy);
And this seems to work just as well:
fscanf(f, "%f %f%*[ \n\t\r\f\v]\n", &sx, &sy);
Is there a better way to handle this?
fscanf(f, "%f%f", &one, &two);
while (1) {
ch = fgetc(f);
if (ch == EOF) /* end of input whithout a line break */break;
if (ch == '\n') /* input ok */break;
if (!isspace((unsigned char)ch)) /* bad input */break;
}
I don't know how to do it with a single *scanf, I don't think it's possible due to the last part of your request, *scanf can't read an arbitrary length character sequence matching a simple regex and ending with a specified character.
int character;
bool trailingNewLine;
if (fscanf(f, "%f%f", &sx, &sy) < 2)
// not matching "whitespace-float-whitespace-float"
// being more accurate could be painful...
// read arbitrary quantity of white space ending with '\n'
while (isspace(character = getc(f)))
trailingNewLine = (character == '\n');
// the last one wasn't white space, doesn't belong to this one
ungetc(character, f);
if (!trailingNewLine)
// missing newline at the end
// OK!
If you are specific about how many characters you have to read in(or have any idea max. no. of characters in a line) use:
fread ( stringbuffer, size, count,Filepointer );
Then use:
sscanf() to read in the two float, then use a for loop to count the no. of white spaces in the read string.
You can also use strtok().
NOTE: Reading a input stream until end-of-line(where end-of-line is marked by a new line in your case) including whitespaces in between(because in C, atleast, whitespace does include new lines, too) is too vague. Better read data in chunks in that case using fread and process the stored data till you reach your specific end-of-line.
scanf("%f %f%*c", &sx, &sy);
// %*c will read and discard all eventual characters after the second float
Related
If I'm not wrong, library function int fscanf(FILE *stream, const char *format, ...) works
exactly the same as function int scanf(const char *format, ...) except that it requires stream selection.
For example if I wanted to read two ints from standard input the code would look something like this.
int first_number;
int second_number;
scanf("%d%d", &first_number, &second_number);
There's no point of me adding newline character in between format specifiers even though the second number is entered in next line of input? Function just looks for next decimal integer right? What happens when I enter two characters instead of ints? Why the function sometimes doesn't work if there's a space between format specifiers?
In addition to that. When reading from file with fscanf(..), lets says the txt file contains next lines:
P6
255
1920 1080
Do I need to specify next line characters in fscanf(..)? I read it like this.
FILE *input = ..
char type[2];
int tr;
int width; int height;
fscanf(input, "%s\n", &type);
fscanf(input, "%d\n" &tr);
fscanf(input, "%d %d\n", &width, &height)
Is there a need for \n to signal next line?
Can fscanf(..) anyhow affect any other functions for reading files like fread()? Or is it a good practice to just stick to one function through the whole file?
scanf(...) operates like fscanf(stdin, ....).
Unless '\n', ' ', or other white spaces are inside a "%[...]", as part of a format for *scanf(), scanning functions the same as if ' ', '\t' '\n' was used. (Also for '\v, '\r, '\f.)
// All function the same.
fscanf(input, "%d\n" &tr);
fscanf(input, "%d " &tr);
fscanf(input, "%d\t" &tr);
There's no point of me adding newline character in between format specifiers even though the second number is entered in next line of input?
All format specifiers except "%n", "^[...]", "%c" consume optional leading white-spaces. With "%d" the is no need other than style to code a leading white-space in the format.
Function just looks for next decimal integer right?
Simply: yes.
What happens when I enter two characters instead of ints?
Scanning stops. The first non-numeric input remains in stdin as well as any following input. The *scanf() return value reflects the incomplete scan.
Why the function sometimes doesn't work if there's a space between format specifiers?
Need example. Having spaces between specifiers is not an issue unless the following specifier is one of "%n", "^[...]", "%c".
When reading from file with fscanf(..), .... Do I need to specify next line characters in fscanf(..)?
No. fscanf() is not line orientated. Use fgets() to read lines. fscanf() is challenging to use to read a line. Something like
char buf[100];
int cnt = fscanf(f, "%99[^\n]", buf);
if (cnt == 0) {
buf[0] = 0;
}
if (cnt != EOF) {
cnt = fscanf(f, "%*1[^\n]");
}
I read it like this. ... fscanf(input, "%s\n", &type); fscanf(input, "%d\n" &tr); ....
"it" as in a line is not read properly as "%s", "%d", "\n" all read consume 0, 1, 2, ... '\n' and other white-spaces. They do not read a line nor just the 1 character of the format.
Further "\n" does not complete upon reading 1 '\n', but continues reading all white-spaces until a non-white-space is detected (or end-of-file). Do not append such to the end of a format to read the rest of the line.
If want to read the trailing '\n', code could use int cnt = fscanf(input, "%d%*1[\n]" &tr);, but code will not know if it succeeded in reading the trailing '\n' after the int. It will have simply read it if it was there. Could use other formats, but really, using fgets() to read a line is better.
Is there a need for \n to signal next line?
No, as a format "\n" reads 0 or more whites-spaces, not just new-lines.
Can fscanf(..) anyhow affect any other functions for reading files like fread()?
Yes. All input function affect what is available next for other input functions. Mixing fread() and fscanf() is challenging to get right.
is it a good practice to just stick to one function through the whole file?
It certainly is simpler. I recommend to use input functions as building blocks for a helper function to handle your file input.
Tip: Read lines with fgets(), then parse. Set fscanf() aside until you understand why it has so much trouble with unexpected input.
The %d conversion specifier tells scanf and fscanf to skip over any leading whitespace, then read up to the first non-digit character, so you don’t need to put a newline between the two %d in the scanf call - in fact, if you do that, it means you have to have a newline between your inputs, not just blanks.
Most conversion specifiers skip over leading whitespace - the only ones that don’t are %c and %[, so you’ll want to be careful when using them.
How do you scanf several words (with spaces in between and an arbitrary number) into a string and not get the '\n' character in the end? I know similar questions has been asked but none of them gave a really satisfying answer. I hope to get an answer to achieve such mechanism in one statement.
char buffer[256];
if (scanf(" %255[^\n]", buffer) != 1)
…oops — EOF or something dramatically awry…
The scan set doesn't skip leading white space (neither does %c or %n), so I added the leading blank to skip leading white space. If you want the leading spaces too, drop that space in the format string, but the onus is on you to ensure that the next character in the input is not a newline (which it often will be if you've just read a number, for example). The conversion (scan set) stops when a newline is reached, or at EOF, or when 255 characters have been read. You could add %*[\n] to read the newline if the next character is a newline. You won't ever know whether that matched or not, though. If you must know, you need:
char buffer[256];
char nl[2];
int rc;
if ((rc = scanf(" %255[^\n]%[\n]", buffer, nl)) <= 0)
…oops — EOF or something dramatically awry…
else if (rc == 1)
…no newline — presumably the input line was longer than 255 characters…
else
…data in buffer is a complete line except for the newline, but the newline was read…
Note the use of 255 vs 256 — that is not an accident but is 100% necessary.
Is it possible to read an entire string including blank spaces like gets() function in scanf()?
I am able to do it using the gets() function.
char s[30];
gets(s);
This will read a line of characters. Can this be done in scanf()?
You can read a line, including blank spaces, with scanf(), but this function is subtle, and using it is very error-prone. Using the %[^\n] conversion specifier, you can tell scanf() to match characters to form a string, excluding '\n' characters. If you do this, you should specify a maximum field width. This width specifies the maximum number of characters to match, so you must leave room for the '\0' terminator.
It is possible that the first character in the input stream is a '\n'. In this case, scanf() would return a value of 0, since there were no matches before encountering the newline. But, nothing would be stored in s, so you may have undefined behavior. To avoid this, you can call scanf() first using the %*[\n] conversion specifier, discarding any leading '\n' characters.
After the string has been read, there will be additional characters in the input stream. At least a '\n' is present, and possibly more characters if the user entered more than the maximum field width specifies. You might then want to discard these extra characters so that they don't interfere with further inputs. The code below includes a loop to do this operation.
The first call to scanf() will consume all newline characters in the input stream until a non-newline character is encountered. While I believe that the second call to scanf() should always be successful, it is good practice to always check the return value of scanf() (which is the number of successful assignments made). I have stored this value in result, and check it before printing the string. If scanf() returns an unexpected result, an error message is printed.
It is better, and easier, to use fgets() to read entire lines. You must remember that fgets() keeps the trailing newline, so you may want to remove it. There is also a possibility that the user will enter more characters than the buffer will store, leaving the remaining characters in the input stream. You may want to remove these extra characters before prompting for more input.
Again, you should check the return value of fgets(); this function returns a pointer to the first element of the storage buffer, or a NULL pointer in the event of an error. The code below replaces any trailing newline character in the string, discards extra characters from the input stream, and prints the string only if the call to fgets() was successful. Otherwise, an error message is printed.
#include <stdio.h>
int main(void)
{
char s[30];
int result;
printf("Please enter a line of input:\n");
scanf("%*[\n]"); // throw away leading '\n' if present
result = scanf("%29[^\n]", s); // match up to 29 characters, excluding '\n'
/* Clear extra characters from input stream */
int c;
while ((c = getchar()) != '\n' && c != EOF)
continue; // discard extra characters
if (result == 1) {
puts(s);
} else {
fprintf(stderr, "EOF reached or error in scanf()\n");
}
printf("Please enter a line of input:\n");
char *ps = fgets(s, 30, stdin); // keeps '\n' character
if (ps) {
while (*ps && *ps != '\n') {
++ps;
}
if (*ps) { // replace '\n' with '\0'
*ps = '\0';
} else {
while ((c = getchar()) != '\n' && c != EOF)
continue; // discard extra characters
}
puts(s);
} else {
fprintf(stderr, "EOF reached or error in fgets()\n");
}
return 0;
}
Note that these two methods of getting a line of input are not exactly equivalent. The scanf() method, as written here, does not accept an empty line (i.e., a line consisting of only the '\n' character), but does accept lines consisting of other whitespace characters. The fscanf() method will accept an empty line as input.
Also, if it is acceptable to ignore leading whitespace characters, it would be simpler to follow the recommendation given by Jonathan Leffler in the comments to use only a single call to scanf():
result = scanf(" %29[^\n]", s);
This will ignore leading whitespace characters, including newlines.
Do not use scanf() or gets() function — use fgets() instead. But for the above question please find the answer.
int main() {
char a[30];
scanf ("%29[^\n]%*c", name);
printf("%s\n", a);
return 0;
}
Its also highly recommended like I told in the beginning to use fgets() instead. We clearly do not understand the weird requirement. I would have used the fgets() to read the character.
fgets(a, size(a), stdin);
how can I enforce following input restriction in c?
First line contains float ,
Second line contains float ,
Third line int,
after pressing enter three times in console, program should be able to read each line and put the contents in respective int,int,float variables.
After three enter key press program should not wait for user input and start validation.
some test cases
line1: 34
line2:4
line3:12
result: ok
line1:
line2:4
line3:12
result: not ok
line1: Hi
line2:4
line3:12
result: not ok
so far I used the basics
scanf("%f",&p);
scanf("%f",&r);
scanf("%d",&t);
it works fine for test case 1 and 3, but fails when I leave an empty line.
You should always check the return value of scanf.
The reason is that the return value is what scanf uses to communicate conversion errors, among other errors. For example, if your program tells scanf to expect a sequence of decimal digits, and scanf encounters something that doesn't match that pattern, the return value will indicate this failure.
The value returned will be the number of items that are successfully assigned to. For example,
char str[128];
int x, y = scanf("%d %127s", &x, str);
If y is 1, then it should be assumed that x is safe to use. If y is 2, then it should be assumed that both x and str are safe to use.
This answers part of your question. The next part is how you can go about ensuring that the input is in the form of lines. scanf doesn't strictly deal with lines; it deals with other units, such as %d being an int encoded as a sequence of decimal digits (and a sign); it'll return once the decimal digit sequence ends... There's no guarantee that the decimal digits will occupy the entirety of the line.
There are actually two problems here: leading and trailing whitespace. All format specifiers, with the exception of [, c, C, and n, will cause leading whitespace to be discarded. If you want to handle leading whitespace differently, you'll need to codify how you expect leading whitespace to be handled.
Consider that discarding user input is almost always (if not always) a bad idea. If you don't care what the remainder of the line contains, you could use something like scanf("%*[^\n]"); getchar(); to discard everything trailing up to and including the '\n' newline character... The first statement would attempt to read as many non-newline characters as possible, and the second would discard the terminating newline character. However, if you want to ensure that the input occupies the entirety of the line, then you need to test the value returned by getchar.
An example using all of these considerations:
/* Test for leading whitespace (including newlines) */
int c = getchar();
if (c != '-' && !isdigit(c)) {
/* Leading whitespace found */
}
ungetc(c);
/* Test for correct data conversion */
int x, y = scanf("%d", &x);
if (y != 1) {
/* Something non-numeric was entered */
}
/* Test for trailing newline */
c = getchar();
if (c != '\n') {
/* Trailing newline found */
}
Armed with this information, perhaps you can come up with an attempt and update your question with some code if you have any problems...
P.S. I noticed in the code you wrote, you seem to have %f and %d confused; %f is for reading into floats, and %d is for reading into ints, not the other way around...
As soon as I read line wise input, I know that fgets + sscanf must be used instead of direct scanf. Of course you can use getc/getchar as a workaround, but you can get corner cases, where I findfgets + sscanf cleaner. Example to get a float alone on a line:
char line[80], dummy[2];
float val;
if (fgets(line, sizeof(line), stdin) == NULL)...
if (sscanf(line, "%f%1s", &val, dummy) != 1)...
// Ok val was alone on the line with optional ignored blanks before and/or after
You could also add a test for loooong lines :
if ((line[0] != 0) && (line[strlen(line)-1 != '\n'))...
I have the following c code
if (sscanf(input, "s %d\n", &value) == 1){}
Where it is suppose to parse the following
s -inf
....
s 0
s 1
s 2
....
s inf
but not such things as
s 5junkjunkjunk
Because it shouldn't match as there is something in between %d and the \n. Yet it does work even though it doesn't fit the format string.
This is (one of the) reasons why one should never use *scanf: it's ridiculously difficult to get it to handle malformed input robustly.
The correct way to parse something like this is: use fgets (or getline if you have it) to read an entire line, manually check for and skip over the leading "s ", then use strtod to parse the number, then check whether strtod set *endp to point to a newline.
If the syntax is even a little more complicated than what you have it may be time to reach for lex and yacc.
If you read the docs for scanf(), you'll find that every appearance of a whitespace character in the format string matches any number of whitespace characters (including zero) in the input. sscanf() can't be forced to match an entire string -- it "succeeds" if it matches everything in the format string, even if this leaves other characters (junkjunkjunk) unconsumed.
After accept answer
inf is nicely read as a floating point.
Agree scanf() is cumbersome, but sscanf() is versatile.
Note: "%n" reports the number of char scanned and does not contribute to sscanf() result.
char buf[100];
if (fgets(buf, sizeof buf, input) == NULL) EOForIOerror();
int n;
double x;
if (sscanf("s %lf %n", &x, &n) != 1 || buf[n]) ScanFailure();
// if needed
if (!isinf(x) && round(x) != x) NumberIsNotInfinityNorWholeNumber();
// x is good to go
Details:
If no legit number is found, sscanf() does not return 1 and ScanFailure() is called.
Else is a legit number is found, " %n" scans any number 0 or more trailing white-spaces such as '\n'. Then it sets the offset of the scan in n. This location will be the first non-white-space after the number. If it is not '\0' then ScanFailure() is called.