Problems when reading from a file with fscanf

B

Benedicte

Hi,

I'm getting some problems when using fscanf to read a file.

This is a piece of the program code:

main ()
{
/*** Variable declaration ***/
FILE *vpfile; /*** Data file ***/

struct line
{
char old_al[10];
char new_al[10];
char vp[5];
char new_dl[17];
};
struct line l[1000]={0};
int i=0;
int nl=0; /*** number of lines read ***/
int res=0;

/*** Executable statements ***/
/*** Open VpMove.txt file ***/
vpfile=fopen("/export/home/granite/CAPI/prog/bin/VpMove.txt","r");
if(vpfile==NULL)
{
printf("The file VpMove.txt could not be opened\n");
return;
}
/*** Read datafile ***/
while(fscanf(vpfile,"%s",l.old_al)!=EOF)
{
fscanf(vpfile,"%s",l.new_al);
fscanf(vpfile,"%s",l.vp);
fscanf(vpfile,"%s",l.new_dl);
i++;
}
nl=i-1; /*** undo i++ ***/
for(i=0;i<=nl;i++)
{
printf("Line %d %s\t %s\t %s\t
%s\n",i,l.old_al,l.new_al,l.vp,l.new_dl);
}
fclose(vpfile);
}

The VpMove.txt file looks like this:
003360073 003362061 240 AP-B_LIE_PP01-523
003360073 003351154 244 AP-B_LIE_PP01-10
003360073 003351154 243 AP-B_LIE_PP01

The output is the following:
Line 0 003360073 003362061 240 AP-B_LIE_PP01-523003360073
Line 1 003360073 003351154 244 AP-B_LIE_PP01-10
Line 2 003360073 003351154 243 AP-B_LIE_PP01

The problem is that when the word ends with a minus sign followed by 3
digits (see first line) he takes directly the following word: so
instead of AP-B_LIE_PP01-523 I receive AP-B_LIE_PP01-523003360073.

Can someone help me on this one?????
 
R

Rob van der Leek

Hi,

I'm getting some problems when using fscanf to read a file.

This is a piece of the program code:

It's great you posted a working program, but please rember to
also include apropriate headers, e.g.: 'stdio.h' in this case.
main ()
{
/*** Variable declaration ***/
FILE *vpfile; /*** Data file ***/

struct line
{
char old_al[10];
char new_al[10];
char vp[5];
char new_dl[17];
};
struct line l[1000]={0};
int i=0;
int nl=0; /*** number of lines read ***/
int res=0;

/*** Executable statements ***/
/*** Open VpMove.txt file ***/
vpfile=fopen("/export/home/granite/CAPI/prog/bin/VpMove.txt","r");
if(vpfile==NULL)
{
printf("The file VpMove.txt could not be opened\n");
return;
}
/*** Read datafile ***/
while(fscanf(vpfile,"%s",l.old_al)!=EOF)
{
fscanf(vpfile,"%s",l.new_al);
fscanf(vpfile,"%s",l.vp);
fscanf(vpfile,"%s",l.new_dl);
i++;
}
nl=i-1; /*** undo i++ ***/
for(i=0;i<=nl;i++)
{
printf("Line %d %s\t %s\t %s\t
%s\n",i,l.old_al,l.new_al,l.vp,l.new_dl);
}
fclose(vpfile);
}

The VpMove.txt file looks like this:
003360073 003362061 240 AP-B_LIE_PP01-523
003360073 003351154 244 AP-B_LIE_PP01-10
003360073 003351154 243 AP-B_LIE_PP01

The output is the following:
Line 0 003360073 003362061 240 AP-B_LIE_PP01-523003360073
Line 1 003360073 003351154 244 AP-B_LIE_PP01-10
Line 2 003360073 003351154 243 AP-B_LIE_PP01

The problem is that when the word ends with a minus sign followed by 3
digits (see first line) he takes directly the following word: so
instead of AP-B_LIE_PP01-523 I receive AP-B_LIE_PP01-523003360073.

Can someone help me on this one?????


The problem is that you overflow the character array of 'new_dl' fields.
This array is 17 characterss big, 'AP-B_LIE_PP01-523' has 17 characters,
leaving no space for the '\0' (zero terminator) that fscanf(...) wants
to insert at the end. When you print the value of 'new_dl' you run into
UB since printf expects a zero terminator to end strings. You might
find this zero terminator at the first field of the next record, that
explains the '003360073' part.
Increase the size of 'new_dl' from 17 to 18 and you'll see it works,
this is of course by no means a constructive solution.

Regards,
 
V

Victor Nazarov

Benedicte said:
struct line
{
char old_al[10];
char new_al[10];
char vp[5];
char new_dl[17];
};

So you have allocated 17 charecters for new_dl
fscanf(vpfile,"%s",l.new_dl);


fscanf has wrote 18 charecters to new_dl. So undefined behavior.
Really it has wrote ending '\0' to the old_al of the second array member.
nl=i-1; /*** undo i++ ***/
for(i=0;i<=nl;i++)
{

for (i = 0; i < nl; i++)
is consider to be standart among programmers. So use it insted of
decreasing nl

Using scanf is not safe because of buffer overrun as in your example.
You can use fgets and break the line into tokens by strtok or strpbrk
If you don't like fgets you can use some thing like:

char *afgets (char *s, size_t *lenp, FILE *f)
{
char *t;
ptrdiff_t tmp;
size_t l = 0;

if (lenp)
l = *lenp;
if (!s)
l = 0;
if (l == 0) {
l = 10;
s = malloc (l);
}
t = s;
for (;;) {
fgets (t, l, f);
t += strlen (t) - 1;
if (*t == '\n' || feof (f))
break;
tmp = t - s;
if ((t = realloc (s, l *= 2)) == NULL) {
l /= 2;
break;
}
s = t;
t += tmp;
}
if (lenp)
*lenp = l;
return s;
}
 
A

Al Bowers

Benedicte said:
Hi,

I'm getting some problems when using fscanf to read a file.

This is a piece of the program code:
#include said:
int main(void)
{
/*** Variable declaration ***/
FILE *vpfile; /*** Data file ***/

struct line
{
char old_al[10];
char new_al[10];
char vp[5];
char new_dl[17];
};

You have not made some of the character arrays in the struct large
enough for the data. Looking at the data you supplied:

The VpMove.txt file looks like this:
003360073 003362061 240 AP-B_LIE_PP01-523
003360073 003351154 244 AP-B_LIE_PP01-10
003360073 003351154 243 AP-B_LIE_PP01

The struct, at a minimum should be:

struct line
{
char old_al[11]; /* allows enough for the string ending '\0' char */
char new_al[11];
char vp[4];
char new_dl[18];
};
struct line l[1000]={0};
int i=0;
int nl=0; /*** number of lines read ***/
int res=0;
What is res used for?
/*** Executable statements ***/
/*** Open VpMove.txt file ***/
vpfile=fopen("/export/home/granite/CAPI/prog/bin/VpMove.txt","r");
if(vpfile==NULL)
{
printf("The file VpMove.txt could not be opened\n");
return;

main returns an int. You could make this:
return 1;
}

/*** Read datafile ***/
while(fscanf(vpfile,"%s",l.old_al)!=EOF)
{
fscanf(vpfile,"%s",l.new_al);
fscanf(vpfile,"%s",l.vp);
fscanf(vpfile,"%s",l.new_dl);
i++;
}
nl=i-1; /*** undo i++ ***/
for(i=0;i<=nl;i++)
{
printf("Line %d %s\t %s\t %s\t
%s\n",i,l.old_al,l.new_al,l.vp,l.new_dl);
}


You can modify the above to:

for(nl = 0; 4 == fscanf(vpfile, " %s %s %s %s ",l[nl].old_al,
l[nl].new_al, l[nl].vp, l[nl].new_dl) ; nl++) ;
for(i=0;i < nl;i++)
printf("Line %d %s\t %s\t %s\t %s\n",
i,l.old_al,l.new_al,l.vp,l.new_dl);
fclose(vpfile);

return 0;
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top