I'm using fscanf to parse a string, and I've been using it thusly:
fscanf(file_in, "%i: %s%c%s", ¤t_id) == 4
Ignore the weirdness of the formatting(I know I could probably get away with replacing the %s%c%s with a single %s), because the most important specific is that the number of formatting arguments doesn't match the number of format specifiers in the string itself
The compiler throws a warning about this, and while it runs correctly, I'd like to know if there is a better way of doing this, perhaps another function?
Edit: Some people have asked for it, so here's the basic gist:
I'm writing a Todo app as practice (and for fun)
It saves data in a file, formatted thusly:
{item.id}: {item.title}{Unit Separator}{item.desc}
My current goal with fscanf was to incrementally check to see if the ID I wanted to assign to an item was already taken, and if so, increment current_id.
Nothing about this program is sophisticated in any way, and I don't intend to publish it as anything more serious than what it is: A practice project.
I'm using fscanf to parse a string, and I've been using it thusly:
fscanf(file_in, "%i: %s%c%s", ¤t_id) == 4
Ignore the weirdness of the formatting(I know I could probably get away with replacing the %s%c%s with a single %s), because the most important specific is that the number of formatting arguments doesn't match the number of format specifiers in the string itself
The compiler throws a warning about this, and while it runs correctly, I'd like to know if there is a better way of doing this, perhaps another function?
Edit: Some people have asked for it, so here's the basic gist:
I'm writing a Todo app as practice (and for fun)
It saves data in a file, formatted thusly:
{item.id}: {item.title}{Unit Separator}{item.desc}
My current goal with fscanf was to incrementally check to see if the ID I wanted to assign to an item was already taken, and if so, increment current_id.
Nothing about this program is sophisticated in any way, and I don't intend to publish it as anything more serious than what it is: A practice project.
Share Improve this question edited Mar 25 at 1:07 GooseyLoosey asked Mar 21 at 21:13 GooseyLooseyGooseyLoosey 233 bronze badges 7 | Show 2 more comments3 Answers
Reset to default 4You can use *
right after the %
which indicates assignment suppression, i.e. nothing will be assigned as a result of matching that particular format specifier.
fscanf(file_in, "%i: %*s%*c%*s", ¤t_id) == 1
Note that this will now return the number of items matched and assigned.
Consider using %d
instead of %i
. If there are any values with a leading 0
, %i
will assume the value is base 8 ( octal). Scanning 058
or 059
, using %i
, will only store 5
as the digits 8
and 9
are not valid octal digits. %d
will store 58
or 59
.
if ( fscanf ( file_in, "%d", ¤t_id) == 1)
will be true if an integer is successfully scanned. If an integer isn't scanned, the return will be 0
. The other possible return value is EOF
. This will not detect some errors such as overflow. As mentioned it may be better to use fgets
to read a line, then strtol
to parse the integer to get better error detection.
fscanf ( file_in, "%*[^\n]")
can be used to scan and discard everything up to a newline.
fscanf ( file_in, "%*1[\n]")
will scan and discard one character that must be a newline (if you want to consume the newline). %d
will consume any leading whitespace.
It would be helpful if you would post a few lines from the file.
Something similar to this might be used to read the file.
int result = 0;
do {
if ( 1 == ( result = fscanf ( file_in, "%d", ¤t_id))) {
// do something with current_id
}
if ( EOF == result) {
fprintf ( stderr, "problem EOF\n");
exit ( EXIT_FAILURE);
}
fscanf ( file_in, "*[^\n]"); // scan and discard rest of line up to newline
fscanf ( file_in, "*1[\n]"); // scan and discard one character that must be a newline
} while ( 0 == result);
But without knowing what the file contains, it might not work correctly.
Rather than fscanf(file_in, "%i: %s%c%s", ¤t_id) == 4
, use fgets()
to read a line from the file into a string. Then parse the string. Use " %n"
to determine if the entire line was properly parsed.
#define LINE_SIZE_MAX 256
char buf[LINE_SIZE_MAX];
if (fgets(buf, sizeof buf, file_in)) {
int n = 0;
sscanf(buf, "%i: %*s%*c%*s %n", ¤t_id, &n);
// v---v---------------------- Did scan reach the end of the format?
// | | v------------v---- Was that end of scanning the end of the line?
if (n > 0 && buf[n] == '\0') {
// Success
printf("current_id: %d\n", current_id);
} else {
// Failure
}
fgets()
and then analysing the string input. – Weather Vane Commented Mar 21 at 21:22"%i: %s%c%s"
has 4 conversion specifiers but you only provide a single parameter for conversion. That's undefined behaviour -- it doesn't run"correctly"
at all. – G.M. Commented Mar 21 at 21:23%s%c%s
with a single%s
, even if you suppress assignment as one of your answers describes. The two have meaningfully different behavior. However, you mostly could replace%s%*c%s
with%s%s
. – John Bollinger Commented Mar 22 at 0:45scanf
family of functions is well documented in numerous places. Google "man 3 scanf" for some of them. As for the particular format%s%c%s
: it scans two separate whitespace-delimited strings plus one of the whitespace characters between. Additionally, leading whitespace is discarded by%s
(but not by%c
), so there might be any amount of whitespace between, possibly including newlines. On the other hand,%s
scans only one (whitespace-delimited) string. The two formats will consume different amounts of the input (unless the input is exhausted first). – John Bollinger Commented Mar 25 at 3:29