tags:

views:

96

answers:

4

I've a text file with following contents:

"abc","def","ghi"

The following works to read the file contents properly:

int main()
{
    char name[1024] = {0};
    FILE *file = fopen("file.txt", "r");

    while(1)
    {
        if (fscanf(file, " %[\",]s ", name) == EOF)
            break;
        if (fscanf(file, " %[a-zA-Z]s ", name) == EOF)
            break;

        printf("%s\n", name);
    }

    return 0;
}

However, the following fails:

int main()
{
    char name[1024] = {0}, garbage[5];
    FILE *file = fopen("file.txt", "r");

    while(1)
    {
        if (fscanf(file, " %[\",]s%[a-zA-Z]s ", garbage, name) == EOF)
            break;

        printf("%s\n", name);
    }

    return 0;
}

I'm using MSVC++ 08. What am I missing? I'm looking for a solution with single fscanf() in the while loop. Thanks in advanced.

+4  A: 

It works??? Pure bad luck :-)

Your conversion specifications mean

" %[\",]s "
         ^= optionally skip whitespace
        ^== read a literal 's'
  ^^^^^^=== read an unlimited string of quotes and commas
 ^========= optionally skip whitespace

and

" %[a-zA-Z]s "
            ^= optionally skip whitespace
           ^== read a literal 's'
  ^^^^^^^^^=== read an unlimited string of letters
 ^============ optionally skip whitespace

and

" %[\",]s%[a-zA-Z]s "
                   ^= optionally skip whitespace
                  ^== read a literal 's'
         ^^^^^^^^^=== read an unlimited string of letters
        ^============ read a literal 's'
  ^^^^^^============= read an unlimited string of quotes and commas
 ^=================== optionally skip whitespace

I think you want

" %4[\",]%1023[a-zA-Z] "
                      ^= optionally skip whitespace
         ^^^^^^^^^^^^^== read a string of at most 1023 letters
  ^^^^^^^=============== read a string of at most 4 quotes and commas
 ^====================== optionally skip whitespace

Other than that, scanf returns the number of successful conversions or EOF on error. You're comparing the result value with EOF when you should be comparing to 1 (or 2, or whatever): compare to the number of conversions you expect.

if (scanf() == 3) /* expected 3 conversions */
{ /* ok */ }
else { /* oops, something went wrong */ }
pmg
i thought the 's' is of 'string'. :( i actually totally forgot how [] works. couldn't find it using google. and for pure bad luck ;) it worked out!
Donotalo
A: 

the following should work:

      if (fscanf(file, " %[\",]%[a-zA-Z] ", garbage, name) == EOF) 
        break; 
it outputs *ghi* twice.
Donotalo
+2  A: 

Remove the "s" conversion flag, as in:

if (fscanf(file, " %[\",]%[a-zA-Z] ", garbage, name) < 2)

Note that I must compare to 2 rather than EOF since the last quotation mark will get read-in on the next iteration.

EDIT: I'm surprised your first code sample works too, but it ran just fine with gcc on Mac OS X, so this isn't a Microsoft-specific issue.

chrisaycock
+3  A: 
  1. Brackets are their own conversion specifier, not a modifier. %[a-zA-Z]s means "match any number of alphabetic characters and assign to name, then match a literal s. Remove the s characters.
  2. If you want to match something but discard it, use asterisk and not a garbage buffer: %*[\",].
  3. scanf won't return EOF if at least one specifier matched before the file ended. This means you'll get an erroneous loop when the file pointer is after the i. Consider testing the count of specifiers assigned, or another "%*[\"]" at the end to slurp up the trailing quotes.

This is also why the first version worked. The literal s failed to match, but the first conversion succeeded, so you got name but not EOF.


if (fscanf(file, " %*[\",]%[a-zA-Z] ", name) < 1)
    break;

or

fscanf(file, " %*[\",]%[a-zA-Z]%*[\"] ", name)
Cirno de Bergerac
it is great to learn about `%*[...]`. thanks :)
Donotalo