fscanf and linked list problem

K

Kay

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 ?
 
T

Tiago Quelhas

Kay said:
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.
 
A

Al Bowers

Kay said:
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;
}
 
K

Kay

can it be done by char * name; ?

Al said:
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;
}
 
C

Chris Torek

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

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).
 
C

CBFalconer

Kay said:
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.
 
D

Derrick Coetzee

Chris said:
[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.
 
F

fb

Derrick said:
Chris said:
[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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top