Tony Stark
Tony Stark

Reputation: 25538

C - how to get fscanf() to determine whether what it read is only digits, and no characters

Imagine I have a csv with and each value is an integer. so the first value is the INTEGER 100.

I want fscanf() to read this line, and either tell me it's an integer ONLY, or not. So, it would pass 100 but fail on 100t. What i've been trying to get work is "%d," where the comma is the delimiter of my CSV. so the whole function is

fscanf(fp, "%d,", &count)

Unfortunately, this fails to fail on '100t,' works on '100' and works on 't'. so it just isn't distinguishing between 100 and 100t (all of these numbers are followed by commas, of course

Upvotes: 1

Views: 1900

Answers (6)

John Bode
John Bode

Reputation: 123448

The scanf() family of functions aren't very good at detecting this kind of error. It's not impossible (see Jerry Coffin's answer, which works but is IMO difficult to generalize), but IMO it's not that robust. The better option is to use fgets() to read input as text, tokenize with strtok() or similar, and then use strtol() or strtod() to convert tokens to numerical values:

char buffer[LINE_SIZE];
while (fgets(buffer, sizeof buffer, inFile))
{
  char *token;
  char *newline = strchr(buffer, '\n');
  if (newline) 
    *newline = 0;
  token = strtok(buffer, ",");
  while (token)
  {
    char *chk;
    int value = (int) strtol(token, &chk, 10);
    if (!isspace(*chk) && *chk != 0)
    {
      printf("%s is not a valid integer\n", token);
    }
    else
    {
      printf("successfully read integer value %d\n", val);
    }
    token = strtok(NULL, ",");
  }
}

if (feof(inFile))
{
  printf("Hit end-of-file\n");
}
else
{
  printf("Error during read\n");
}

Upvotes: 0

Jerry Coffin
Jerry Coffin

Reputation: 490108

fscanf is actually much more usable than some of the other answers would imply -- but most people don't know it very well, and don't know how to exercise its full capabilities.

Useful points: first of all, use the return value from fscanf -- it tells you how many items were converted. Second, the "scan set" conversion can be extremely useful. Consider the following (I've used sscanf to avoid requiring an external file, but fscanf differs only in the source from which it reads):

#include <stdio.h>

int main() { 
    int i;
    char *test[] = {
        "100,",    // should succeed.
        "100t,",   // should fail.
        "t"        // should also fail.
    };

    for (i=0; i<3; i++) {
        int count;
        char ch[2];
        if (2 == sscanf(test[i], "%d%[,]", &count, &ch))
            fprintf(stderr, "Conversion of \"%s\" succeeded.\n", test[i]);
        else
            fprintf(stderr, "Conversion of \"%s\" failed.\n", test[i]);
    }
    return 0;
}

Upvotes: 5

mcabral
mcabral

Reputation: 3558

What about

fscanf(fp, "%d%c", &count, &aChar)

if aChar != ',' && != '\n' then you don't have only an integer

Upvotes: 1

Chris Lutz
Chris Lutz

Reputation: 75389

You don't.

The problem is that fscanf() isn't very useful. The best way to handle it is to read in an entire line (or significant chunk of the line) and then analyze the string. Here's an example:

int value;
char *extra;
char buffer[100];

// read in some data from the buffer
fgets(buffer, sizeof buffer, stdin);

// parse out a digit, if we can
i = strtol(buffer, &extra, 0);

At this point, you can check extra to see if there are any extra characters, meaning the line wasn't purely a number, or if extra points to the beginning of buffer, meaning there was no number to parse.

Upvotes: 5

AndersK
AndersK

Reputation: 36082

scanf-functions on input that is not 100% controlled can be a pain to get error free, it is better to read the line using fgets() then use strtok() to split up the line in tokens which then can be converted.

using atoi on one token like "100t" would then yield 0 whereas "100" would yield 100

Upvotes: 0

Marcelo Cantos
Marcelo Cantos

Reputation: 185852

Use strtol instead.

Upvotes: 7

Related Questions