cfilefile-pointer

How to read specific chars and ints from file (update question)?


I have an input file:

H
3(3,3)
V
1(5,4)
2(7,7)

I am reading this with:

 12 FILE *fp = fopen (argv[1], "r");
 13   if (fp == NULL)
 14   {
 15     return EXIT_FAILURE;
 16   }
 17   Stack* top = NULL;
 18   char gets[1024];
 19   while (fgets (gets, 1024, fp))
 20   {
 21     char node;
 22     int width = 0;
 23     int height = 0;
 24     int check = (sscanf (gets, " %c(%d,%d", &node, &width, &height));
 25     if (check == 1)
 26     {
 27       push(&top,node,0,0);
 28     }
 29     else if (check == 3)
 30     {
 31        push(&top,node,width,height);
 32     }
 33   }
 34   fclose (fp);

Which works fine.

  1 3(3,3)
  2 1(5,4)
  3 2(7,7)
  4 V
  5 H

But when I read something like this(input shortened as the file is big):

  1 93(11,16)
  2 11(12,33)
  3 H
  4 34(7,11)
  5 10(9,27)
  6 V
  7 32(12,30)
  8 30(12,16)
  9 41(12,19)
 10 H
 11 V
 12 50(12,13)

I get this, for example:

  1 9(0,0)
  2 1(0,0)
  3 H
  4 3(0,0)
  5 1(0,0)
  6 V
  7 3(0,0)
  8 3(0,0)
  9 4(0,0)
 10 H
 11 V
 12 5(0,0)

How can I fix this error to put the right output - so I get the proper value of a node instead of getting 9 instead of 93 for example. Edit: Its not working, I get something like this:

20 45(11,16)
 21 45(11,16)
 22 45(11,16)
 23 45(11,16)
 24 45(11,16)
 25 45(11,16)
 26 45(11,16)
 27 45(11,16)
 28 45(11,16)
 29 45(11,16)
 30 45(11,16)
 31 45(11,16)
 32 45(11,16)
 33 45(11,16)
 34 45(11,16)
 35 45(11,16)
 36 45(11,16)
 37 45(11,16)
 38 45(11,16)
 39 45(11,16)
 40 45(11,16)
 41 45(11,16)
 42 45(11,16)
 43 45(11,16)
 44 45(11,16)
 45 45(11,16)
 46 45(11,16)
 47 45(11,16)

which isnt even an input.


Solution

  • In this iteration of your question, what is changed is instead of having only a single-character before the first "(" or on a line by itself, you can now have multiple characters. You still want to be able to read either 'H' or 93 as the first part of the line, so you need to adjust your format-string to read multiple character before the first "(" and then either treat that as a string if it is an alpha-character, or convert it to an integer values if it is composed of digits. You can use something like:

    #define MAXN   32
    ...
        while (fgets (buf, MAXC, fp)) {
            char tmp[MAXN];
            int node, width, height;
            switch (sscanf (buf, " %31[^(\n](%d,%d", tmp, &width, &height)) {
               ...
    

    The format string being " %31[^(\n](%d,%d" where the '\n' is included in the negated character class along with '(' to trim the '\n' from those lines that just contain a single character. The circumflex (e.g. '^') at the beginning of the [^..] telling you to read character not including what is contained within the character class. (e.g. stop reading when the first negated character is encountered)

    A full example would be:

    #include <stdio.h>
    
    #define MAXC 1024   /* if you need a constant, #define one (or more) */
    #define MAXN   32
    
    int main (int argc, char **argv) {
    
        char buf[MAXC];
        /* use filename provided as 1st argument (stdin by default) */
        FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    
        if (!fp) {  /* validate file open for reading */
            perror ("file open failed");
            return 1;
        }
    
        while (fgets (buf, MAXC, fp)) {
            char tmp[MAXN];
            int node, width, height;
            switch (sscanf (buf, " %31[^(\n](%d,%d", tmp, &width, &height)) {
                case 1 : printf ("single char/digit: '%s'\n", tmp);
                        break;
                case 3 : if (sscanf (tmp, "%d", &node) == 1)
                            printf ("all values: %d  %d  %d\n", node, width, height);
                        else
                            fputs ("error: parsing node from tmp.\n", stderr);
                        break;
                default : fputs ("invalid line format\n", stderr);
            }
        }
        if (fp != stdin)   /* close file if not stdin */
            fclose (fp);
    }
    

    You can change the switch() statement to your if/else statements.

    Example Use/Output

    With your updated input, you would now get:

    $ ./bin/fgets_sscanf_multival2 dat/multivals2.txt
    all values: 93  11  16
    all values: 11  12  33
    single char/digit: 'H'
    all values: 34  7  11
    all values: 10  9  27
    single char/digit: 'V'
    all values: 32  12  30
    all values: 30  12  16
    all values: 41  12  19
    single char/digit: 'H'
    single char/digit: 'V'
    all values: 50  12  13
    

    Where you can push all value to your stack where needed. Let me know if you have further questions.