Lenny
Lenny

Reputation: 193

Getting particular strings in scanf

I was wondering if it is possible to only read in particular parts of a string using scanf.

For example since I am reading from a file i use fscanf

if I wanted to read name and number (where number is the 111-2222) when they are in a string such as:

Bob Hardy:sometext:111-2222:sometext:sometext

I use this but its not working:

(fscanf(read, "%23[^:] %27[^:] %10[^:] %27[^:] %d\n", name,var1, number, var2, var3))

Upvotes: 0

Views: 362

Answers (2)

chux
chux

Reputation: 154280

Check fscanf() return value.

fscanf(read, "%23[^:] %27[^:] ... is failing because after scanning the first field with %23[^:], fscanf() encounters a ':'. Since that does not match the next part of the format, a white-space as in ' ', scanning stops.

Had code checked the returned value of fscanf(), which was certainly 1, it may have been self-evident the source of the problem. So the scanning needs to consume the ':', add it to the format: "%23[^:]: %27[^:]: ...

Better to use fgets()

Using fscanf() to read data and detect properly and improperly formatted data is very challenging. It can be done correctly to scan expected input. Yet it rarely works to handle some incorrectly formated input.

Instead, simple read a line of data and then parse it. Using '%n' is an easy way to detect complete conversion as it saves the char scan count - if scanning gets there.

char buffer[200];
if (fgets(buffer, sizeof buffer, read) == NULL) {
  return EOF;
}
int n = 0;
sscanf(buffer, " %23[^:]: %27[^:]: %10[^:]: %27[^:]:%d %n", 
    name, var1, number, var2, &var3, &n);
if (n == 0) {
  return FAIL; // scan incomplete
}
if (buffer[n]) {
  return FAIL; // Extra data on line
}
// Success!

Note: sample input ended with text, but original format used "%d". Unclear on OP's intent.

Upvotes: 1

John Bollinger
John Bollinger

Reputation: 180968

Your initial format string fails because it does not consume the : delimiters.

If you want scanf() to read a portion of the input, but you don't care what is actually read, then you should use a field descriptor with the assignment-suppression flag (*):

char nl;
fscanf(read, "%23[^:]:%*[^:]:%10[^:]%*[^\n]%c", name, number, &nl);

As a bonus, you don't need to worry about buffer overruns for fields with assignment suppressed.

You should not attempt to match a single newline via a trailing newline character in the format, because a literal newline (or space or tab) in the format will match any run of whitespace. In this particular case, it would consume not just the line terminator but also any leading whitespace on the next line.

The last field is not suppressed, even though it will almost always receive a newline, because that way you can tell from the return value if you've scanned the last line of the file and it is not newline-terminated.

Upvotes: 1

Related Questions