fscanf and linked list problem

Discussion in 'C Programming' started by Kay, Aug 29, 2004.

  1. Kay

    Kay Guest

    1) If i want to read data from a txt file,
    eg John; 23; a
    Mary; 16; i
    How can I read the above data stopping reading b4 each semi-colon and
    save it in three different variables ?

    2) If I enter a number, can I use to call a particular node ?
    eg enter a number: 3
    calling node of number 3
    is it possible ?
    Kay, Aug 29, 2004
    #1
    1. Advertising

  2. Kay wrote:
    > 1) If i want to read data from a txt file,
    > eg John; 23; a
    > Mary; 16; i
    > How can I read the above data stopping reading b4 each semi-colon and
    > save it in three different variables ?


    Each line can be parsed with (your data types may vary):

    fscanf(fp, "%s; %i; %c\n", &var1, &var2, &var3);

    >
    > 2) If I enter a number, can I use to call a particular node ?
    > eg enter a number: 3
    > calling node of number 3
    > is it possible ?
    >


    I don't fully understand the question. Do you have a linked list and
    want to find the n-th node? Start at the root node and do something like
    node = node->next; n times.

    --
    Tiago
    Tiago Quelhas, Aug 29, 2004
    #2
    1. Advertising

  3. Kay

    Al Bowers Guest

    Kay wrote:
    > 1) If i want to read data from a txt file,
    > eg John; 23; a
    > Mary; 16; i
    > How can I read the above data stopping reading b4 each semi-colon and
    > save it in three different variables ?
    >
    > 2) If I enter a number, can I use to call a particular node ?
    > eg enter a number: 3
    > calling node of number 3
    > is it possible ?
    >


    You could try reading a line of text from the file with function
    fgets. Then use function sscanf to parse the line. The specifier
    %s can be used if the name has no spaces in it. Otherwise you can
    use the scanset, []. If you want the number to be type int use
    %d for the specifier, or %u if unsigned. Then for the last use %c.

    For an example look at function AddBIO_ARR in the code below.

    #include <stdio.h>
    #include <stdlib.h>

    #define LINE_MAX 80

    typedef struct BIO
    {
    char name[64];
    unsigned age;
    char paylocation;
    } BIO;

    typedef struct BIO_ARR
    {
    BIO *bio;
    unsigned cnt;
    }BIO_ARR;

    int AddBIO_ARR(BIO_ARR *p, const char *s);
    void PrintBIO_ARR( BIO_ARR *p);
    void FreeBIO_ARR( BIO_ARR *p);

    int main(void)
    {
    char fline[LINE_MAX];
    struct BIO_ARR Prez = {NULL,0}; /* Initializes with no Bio Info */
    FILE *fp;

    if((fp = fopen("test.txt","r")) == NULL)
    {
    puts("Unable to open the file");
    exit(EXIT_FAILURE);
    }
    while(NULL != fgets(fline,sizeof fline,fp))
    AddBIO_ARR(&Prez,fline);
    fclose(fp);
    PrintBIO_ARR(&Prez);
    FreeBIO_ARR(&Prez);
    return 0;
    }

    int AddBIO_ARR(BIO_ARR *p, const char *s)
    {
    BIO *tmp;

    if((tmp = realloc(p->bio,(sizeof *p->bio)*(p->cnt+1))) == NULL)
    return 0;
    p->bio = tmp;
    if(3 != sscanf(s,"%63[^;]; %u; %c",p->bio[p->cnt].name,
    &p->bio[p->cnt].age, &p->bio[p->cnt].paylocation))
    return 0;
    p->cnt++;
    return 1;
    }

    void PrintBIO_ARR( BIO_ARR *p)
    {
    unsigned i;

    for(i = 0; i < p->cnt;i++)
    printf("Name: %s\n Age: %u\nPLoc: %c\n\n",
    p->bio.name, p->bio.age,
    p->bio.paylocation);
    return;
    }

    void FreeBIO_ARR( BIO_ARR *p)
    {
    free(p->bio);
    p->bio = NULL;
    p->cnt = 0;
    return;
    }


    --
    Al Bowers
    Tampa, Fl USA
    mailto: (remove the x to send email)
    http://www.geocities.com/abowers822/
    Al Bowers, Aug 29, 2004
    #3
  4. Kay

    Kay Guest

    can it be done by char * name; ?

    Al Bowers wrote:
    >
    >
    > Kay wrote:
    >
    >> 1) If i want to read data from a txt file,
    >> eg John; 23; a
    >> Mary; 16; i
    >> How can I read the above data stopping reading b4 each semi-colon and
    >> save it in three different variables ?
    >>
    >> 2) If I enter a number, can I use to call a particular node ?
    >> eg enter a number: 3
    >> calling node of number 3
    >> is it possible ?
    >>

    >
    > You could try reading a line of text from the file with function
    > fgets. Then use function sscanf to parse the line. The specifier
    > %s can be used if the name has no spaces in it. Otherwise you can
    > use the scanset, []. If you want the number to be type int use
    > %d for the specifier, or %u if unsigned. Then for the last use %c.
    >
    > For an example look at function AddBIO_ARR in the code below.
    >
    > #include <stdio.h>
    > #include <stdlib.h>
    >
    > #define LINE_MAX 80
    >
    > typedef struct BIO
    > {
    > char name[64];
    > unsigned age;
    > char paylocation;
    > } BIO;
    >
    > typedef struct BIO_ARR
    > {
    > BIO *bio;
    > unsigned cnt;
    > }BIO_ARR;
    >
    > int AddBIO_ARR(BIO_ARR *p, const char *s);
    > void PrintBIO_ARR( BIO_ARR *p);
    > void FreeBIO_ARR( BIO_ARR *p);
    >
    > int main(void)
    > {
    > char fline[LINE_MAX];
    > struct BIO_ARR Prez = {NULL,0}; /* Initializes with no Bio Info */
    > FILE *fp;
    >
    > if((fp = fopen("test.txt","r")) == NULL)
    > {
    > puts("Unable to open the file");
    > exit(EXIT_FAILURE);
    > }
    > while(NULL != fgets(fline,sizeof fline,fp))
    > AddBIO_ARR(&Prez,fline);
    > fclose(fp);
    > PrintBIO_ARR(&Prez);
    > FreeBIO_ARR(&Prez);
    > return 0;
    > }
    >
    > int AddBIO_ARR(BIO_ARR *p, const char *s)
    > {
    > BIO *tmp;
    >
    > if((tmp = realloc(p->bio,(sizeof *p->bio)*(p->cnt+1))) == NULL)
    > return 0;
    > p->bio = tmp;
    > if(3 != sscanf(s,"%63[^;]; %u; %c",p->bio[p->cnt].name,
    > &p->bio[p->cnt].age, &p->bio[p->cnt].paylocation))
    > return 0;
    > p->cnt++;
    > return 1;
    > }
    >
    > void PrintBIO_ARR( BIO_ARR *p)
    > {
    > unsigned i;
    >
    > for(i = 0; i < p->cnt;i++)
    > printf("Name: %s\n Age: %u\nPLoc: %c\n\n",
    > p->bio.name, p->bio.age,
    > p->bio.paylocation);
    > return;
    > }
    >
    > void FreeBIO_ARR( BIO_ARR *p)
    > {
    > free(p->bio);
    > p->bio = NULL;
    > p->cnt = 0;
    > return;
    > }
    >
    >
    Kay, Aug 29, 2004
    #4
  5. Kay

    Chris Torek Guest

    >Kay wrote:
    >> 1) If i want to read data from a txt file,
    >> eg John; 23; a
    >> Mary; 16; i
    >> How can I read the above data stopping reading b4 each semi-colon and
    >> save it in three different variables ?


    ( http://www.thecoolkids.org/articles/editorials/onlinelang.php )

    In article <news:4131d591$0$1829$>
    Tiago Quelhas <> wrote:
    >Each line can be parsed with (your data types may vary):
    >
    >fscanf(fp, "%s; %i; %c\n", &var1, &var2, &var3);


    No, %s is the wrong format; and "&var1" is almost certainly wrong
    as well. %i is legal but may or may not be what is desired.
    Lastly, the final newline in the scanf directive does not mean what
    I suspect you think it means. :)

    The "%s" directive tells the scanf family of functions to:
    (a) read and consume any leading whitespace, including no
    whitespace, then
    (b) read and convert at least one character, and as many
    characters as possible until the next whitespace. If
    assignment is not suppressed, these will be stored through
    the supplied pointer, writing to *&var, then *((&var)+1),
    then *(&var + 2), and so on; after the last character
    written, scanf will add the string-terminator marker '\0'.

    If "var1" has type "array N of char", the value you want to supply
    to the scanf family is the address of the *first element* of var,
    not the address of "var" as a whole. Due to type conversions inside
    the call, &var is fairly likely to work on most machines, despite
    having undefined behavior in the C standard. More importantly,
    though, if "var1" has type "array 100 of char" for instance:

    char var1[100];

    then even if you fix the call to read:

    ret = fscanf(fp, "%s; %i; %c\n", &var1[0], &var2, &var3);

    the input line "John; 23; a" will write {'J', 'o', 'h', 'n', ';'}
    in sequence to var1[0] through var1[4], and set var1[5] to '\0'.
    The OP asked to have var1[0] through var1[3] set to "John", without
    the semicolon, so var1[4] should hold the '\0'.

    (Of course, without a field-width, this fscanf() call is the next
    Microsoft security hole waiting to be exploited, as well.)

    Now, we can fix this by using scanf's %[ directive to scan for
    "characters not including semicolon". Since %[ does *not* skip
    leading whitespace, we must decide whether to do so ourselves.
    If we choose to skip leading whitespace, and use a fieldwidth
    that is correct for "char var1[100]", we could write:

    ret = fscanf(fp, " %99[^;]; %i; %c\n", var1, &var2, &var3);

    This solves the problem of converting the semicolon, but perhaps
    adds a new one: malformed input lines that contain *no* semicolon
    will be scanned, newline and all, into var1 until var1 fills up.
    For instance, if the text in the input stream at the point of the
    call begins with:

    " hello there\n\tthis is not proper, is it?\nBob;"

    then the " " directive will skip the two initial blanks, but the
    subsequent %[ directive will read and convert the next two entire
    lines plus the word "Bob" off the third line.

    Can this be fixed (assuming it is a problem)? Certainly: just
    add newline to the characters excluded from the scanset, giving
    " %[^;\n]". But as you can see, things are getting complicated
    already.

    A well-behaved application will, in my opinion, handle malformed
    input files, and if possible, give a hint about fixing such files.
    One way to do this is to deliver a message to stderr giving the
    input file name and line number. How can you keep track of the
    line number? You simply have to take some particular action every
    time you cross a newline. The scanf family's whitespace-eating
    directives, however, cause a problem: newlines *are* "whitespace",
    and that leading " " in " %[..." will happily scan right over
    dozens or hundreds of them, without alerting you. We could fix
    that by removing the leading blank, of course, and demanding that
    names like "John" and "Mary" appear left-aligned. But what about
    the rest of the whitespace directives in the format? There are
    four more in "; %i; %c\n", even if two of them are hard to see.
    Where are they? Well, the two blanks are obviously whitespace
    directives. There is also one hidden inside "%i": %i, %d, %f,
    and indeed most of the %-related directives all include one. (The
    two exceptions are %[ and %c.) Finally, the newline at the end
    of the format is also a whitespace directive. To the scanf family,
    there is NO DIFFERENCE AT ALL between " ", "\t", "\n", "\b", and
    so on! (In directives, that is.)

    Call all this also be fixed? Most of it can, at the expense of
    complicating the code enormously:

    ret = fscanf("%99[^;\n];%c%i;%c%c%c", var1, &blank1,
    &var2, &blank2, &var3, &newline1);
    /* now inspect "ret", and if *it* seems OK, then check up on
    blank1, blank2, and newline1 to make sure they are the
    expected two blanks and a newline */

    The "%i" directive is still troublesome. Also, note that %i
    conversions are done as if via strtol() with 0 as a base, so that
    numeric fields that begin with 0 or 0x are treated as octal or
    hexadecimal, as in C. This is often not what one wants with
    user input files (although sometimes it is).

    There is a much better way to do this job, and that is to read
    a complete input line with fgets() or some substitute (such as
    CBFalconer's ggets()), then pick it apart, perhaps even with
    sscanf(). Each time you read one complete input line, you can
    be sure you have read one complete input line -- no more, and
    no less. That makes it much easier to print error messages like:

    input.file, line 47: wrong format -- I can't understand the
    text "this line has no semicolons, oops!"

    with something like:

    fprintf(stderr, "%s, line %d: wrong format -- I can't "
    "understand the text \"%s\"\n", filename, linenumber, inputline);

    (note that this assumes the terminating newline has been stripped
    from the input line).
    --
    In-Real-Life: Chris Torek, Wind River Systems
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: forget about it http://web.torek.net/torek/index.html
    Reading email is like searching for food in the garbage, thanks to spammers.
    Chris Torek, Aug 29, 2004
    #5
  6. Kay

    CBFalconer Guest

    Kay wrote:
    >
    > 1) If i want to read data from a txt file,
    > eg John; 23; a
    > Mary; 16; i
    > How can I read the above data stopping reading b4 each semi-colon
    > and save it in three different variables ?


    You will need either the value 180 or the text string "b4" in the
    file. When you have arranged that I suggest using the getc()
    function.

    --
    A: Because it fouls the order in which people normally read text.
    Q: Why is top-posting such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
    CBFalconer, Aug 29, 2004
    #6
  7. Chris Torek wrote:
    > [a bunch of ad-hoc parsing with scanf]


    Alternatively, use a lexer generator such as GNU flex to parse such
    files in the desired manner, detect malformed input and report it
    correctly, and all such other nice stuff with a smaller description and
    less chance of error. Your program will run faster too. You can then use
    more specific conversion functions like atof on the individual tokens.
    --
    Derrick Coetzee
    I grant this newsgroup posting into the public domain. I disclaim all
    express or implied warranty and all liability. I am not a professional.
    Derrick Coetzee, Sep 2, 2004
    #7
  8. Kay

    fb Guest

    Derrick Coetzee wrote:
    > Chris Torek wrote:
    >
    >> [a bunch of ad-hoc parsing with scanf]

    >
    >
    > Alternatively, use a lexer generator such as GNU flex to parse such
    > files in the desired manner, detect malformed input and report it
    > correctly, and all such other nice stuff with a smaller description and
    > less chance of error. Your program will run faster too. You can then use
    > more specific conversion functions like atof on the individual tokens.


    nit-wit.
    fb, Sep 2, 2004
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Chris Ritchey
    Replies:
    7
    Views:
    466
    emerth
    Jul 10, 2003
  2. Chris Ritchey

    Generating a char* from a linked list of linked lists

    Chris Ritchey, Jul 9, 2003, in forum: C Programming
    Replies:
    7
    Views:
    455
    emerth
    Jul 10, 2003
  3. fool
    Replies:
    14
    Views:
    494
    Barry Schwarz
    Jul 3, 2006
  4. joshd
    Replies:
    12
    Views:
    656
    John Carson
    Oct 2, 2006
  5. jawdoc
    Replies:
    9
    Views:
    736
    Chris Thomasson
    Mar 10, 2008
Loading...

Share This Page