reading data from a file into a 2d array

Y

yourmycaffiene

Okay, this if my first post so go easy on me plus I've only been using
C for a couple of weeks. I'm working on a program currently that
requires me to read data from a .dat file into a 2d array and then
print out the contents of the 2d array to the screen. I wil also need
to append data to the .dat file but mostly right now I'm worrying about
getting the info into the 2d array. My .dat file looks like this

1 20000
2 30000
3 40000

etc. The first number standing for an ID and the second standing for a
weight.

#include <stdio.h>
main()
{
FILE *pRead;
int array[20][3];
pRead = fopen("test.dat", "r");

if (pRead==NULL)
printf("\nFile cannot be opened\n");

else
printf("\nContents of test.dat\n\n");
fscanf(pRead,"%s,%s", array);

while ( !feof(pRead) )
{
printf("%s%s\n", array);
fscanf(pRead, "%s,%s", array);
}

}



I know I need to use a for loop or something to that idea so that I can
read in the column and then the row. I'm really drawing a blank here on
how to get that working. Could someone point me in the right direction?
Cause right now my output is coming out like this :

11
20002000
22
30003000

etc.


Also, if I use %d instead of %s here, I get segmentation faults. Can
anyone explain why this is happening? Thanks.
 
D

darkchild50

do you mind telling me why you used a multi-dimintinal array? to me it
seems also you are
using an integer array and filling it with strings?
 
R

Richard G. Riley

Okay, this if my first post so go easy on me plus I've only been using
C for a couple of weeks. I'm working on a program currently that
requires me to read data from a .dat file into a 2d array and then
print out the contents of the 2d array to the screen. I wil also need
to append data to the .dat file but mostly right now I'm worrying about
getting the info into the 2d array. My .dat file looks like this

1 20000
2 30000
3 40000

etc. The first number standing for an ID and the second standing for a
weight.

#include <stdio.h>
main()
{
FILE *pRead;
int array[20][3];
pRead = fopen("test.dat", "r");

if (pRead==NULL)
printf("\nFile cannot be opened\n");

else
printf("\nContents of test.dat\n\n");
fscanf(pRead,"%s,%s", array);

while ( !feof(pRead) )
{
printf("%s%s\n", array);
fscanf(pRead, "%s,%s", array);
}

}



I know I need to use a for loop or something to that idea so that I can
read in the column and then the row. I'm really drawing a blank here on
how to get that working. Could someone point me in the right direction?
Cause right now my output is coming out like this :

11
20002000
22
30003000

etc.


Also, if I use %d instead of %s here, I get segmentation faults. Can
anyone explain why this is happening? Thanks.

Have a look at this code and work from there:

#include <stdio.h>
main()
{

unsigned long a,b;

FILE * pRead = fopen("test.dat", "r");

if (pRead==NULL)
printf("File cannot be opened\n");

else
printf("Contents of test.dat : \n");



while ( !feof(pRead) ){
fscanf(pRead,"%u %u\n", &a,&b);
printf("%u %u\n",a,b);
}

}


Some pointers to help you on the way : fcanf can do the conversions
for you : use it if you use fscanf. You were reading in strings and
then storing them in "int" storage - this is naughty and very
unclean. Try to get a handle on the types and keep them
concistent. While C can and does allow you to stick data in the wrong
"type memory" it is a very bad idea.

The storage in the array we leave as an excercise :)
 
V

Vladimir S. Oka

yourmycaffiene said:
Okay, this if my first post so go easy on me plus I've only been using
C for a couple of weeks. I'm working on a program currently that
requires me to read data from a .dat file into a 2d array and then
print out the contents of the 2d array to the screen. I wil also need
to append data to the .dat file but mostly right now I'm worrying about
getting the info into the 2d array. My .dat file looks like this

1 20000
2 30000
3 40000

etc. The first number standing for an ID and the second standing for a
weight.

#include <stdio.h>

Vertical space is cheap...

Better spell it out:

int main(void)
{
FILE *pRead;
int array[20][3];

Why 3 as a second dimension if you only have two columns. Avoiding
zero-based indexing in this way is a waste of memory. Imagine if the
first dimension was 200,000.
pRead = fopen("test.dat", "r");

if (pRead==NULL)
printf("\nFile cannot be opened\n");

Vertical space here hinders reading...

You also probably want to abort execution at this point. The way it
stands, you'll still try to read from the file you failed to open.
else
printf("\nContents of test.dat\n\n");

Apart from lack of vertical spacing, you should really indent properly.
E.g.:

else
printf("Blah.\n");
fscanf(pRead,"%s,%s", array);

Quite a few problems here. You're specifying `%s` which expects a
string from the file, but you tell `fscanf` to put it into a
two-dimensional array of `int`s. Apart from failing to do what you
want, along that path lies the monster of buffer overflows. You also
tell `fscanf` to expect two strings, but pass only one address of a
variable to store them in.

BTW, what was wrong with including the above line into `while` loop? As
it stands, you'd have to guarantee the file is not empty (impossible).
Also, if you can guarantee that, a `do` loop would have been clearer.
while ( !feof(pRead) )
{
printf("%s%s\n", array);

Now you're trying to print contents of an array of `int`s as a string.
Also, you're telling `printf` to expect two strings, but only pass it
one pointer (of the wrong type, as well).
fscanf(pRead, "%s,%s", array);

Same comment as for the similar line above. Plus, You should really
have swapped the above two (or dropped this one, and put the first one
like this in the loop).

You should close the file as well:

fclose(pFile);

C99 will let you omit final `return 0;` but it's good practice to spell
things out.

return 0;

This is probably what you want:

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

#define DAT_MAX_LINES 20

int main(void)
{
int array[DAT_MAX_LINES][2];
int n = 0;
int i;

FILE * pRead = fopen("test.dat", "r");

if (!pRead)
{
printf("File cannot be opened\n");
return EXIT_FAILURE;
}

printf("Contents of test.dat:\n");

while ( (n < DAT_MAX_LINES) && (!feof(pRead)) )
{
fscanf(pRead,"%d%d\n", &array[n][0], &array[n][1]);
++n;
}

if ( (!feof(pFile)) && (n == DAT_MAX_FILES) )
{
printf("Error: file to large for array.\n");
}

fclose(pFile);

for (i = 0; i < n; ++i)
{
printf("%d: %d, %d\n", i, array[0], array[1]);
}

return EXIT_SUCCESS;
}

said:
Also, if I use %d instead of %s here, I get segmentation faults. Can
anyone explain why this is happening? Thanks.

See comment above about matching the formats and the parameters (and
the number of the latter). Your compiler should have warned you about
these (mine did). Turn up the warining level as high as you can.
 
B

Ben Bacarisse

Have a look at this code and work from there:

#include <stdio.h>
main()
{

unsigned long a,b;

FILE * pRead = fopen("test.dat", "r");

if (pRead==NULL)
printf("File cannot be opened\n");

else
printf("Contents of test.dat : \n");



while ( !feof(pRead) ){
fscanf(pRead,"%u %u\n", &a,&b);
printf("%u %u\n",a,b);
}

}

This is a dodgy pattern to recommend. You get, I think, undefined
behaviour if you test a NULL FILE * with feof (I get a core dump rather
than any flying demons -- YMMV). You definitely get UB if you test for
end-of-file before reading anything (because there may be no data to read)
and your input is very sensitive to the format used (not technically a
problem, of course, but a beginenr will be baffled by the change in
behaviour caused by switching to "%u %u" from "%u %u\n").

I think the following is more beginner-friendly:

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

int main(void)
{
unsigned long a, b;
FILE *pRead = fopen("test.dat", "r");

if (pRead == NULL)
printf("File cannot be opened\n");
else {
printf("Contents of test.dat : \n");

while (fscanf(pRead,"%u %u", &a,&b) == 2)
printf("%u %u\n", a, b);
}
return 0;
}
 
B

Ben Bacarisse

I think the following is more beginner-friendly:

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

second include is, of course, not needed. I was panning to "exit" and
decided to "return" instead!
 
R

Richard G. Riley

This is a dodgy pattern to recommend. You get, I think, undefined

Thanks. You're right : I missed the return or bracketing at null check -
but then I wasnt looking to write it for him just to indicate the type
problems he had.
 
V

Vladimir S. Oka

Ben Bacarisse wrote:

said:
This is a dodgy pattern to recommend. You get, I think, undefined
behaviour if you test a NULL FILE * with feof (I get a core dump rather

True, as it probably internally dereferences NULL pointer.
than any flying demons -- YMMV). You definitely get UB if you test for
end-of-file before reading anything (because there may be no data to read)

This however I believe is not the case. The Standard says only this:

7.19.10.2p2
The feof function tests the end-of-file indicator for the stream
pointed to by stream.

7.19.10.2p3
The feof function returns nonzero if and only if the end-of-file
indicator is set for
stream.

I see no requirement for any data to exist in the file (think empty
files), or any data to be read beforehand (think empty files again).
and your input is very sensitive to the format used (not technically a
problem, of course, but a beginenr will be baffled by the change in
behaviour caused by switching to "%u %u" from "%u %u\n").

I think the following is more beginner-friendly:

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

int main(void)
{
unsigned long a, b;
FILE *pRead = fopen("test.dat", "r");

if (pRead == NULL)
printf("File cannot be opened\n");
else {
printf("Contents of test.dat : \n");

Here, you make the same mistake as both OP and Richard. Even if the
`fopen` fails you'll try to read from the file.
while (fscanf(pRead,"%u %u", &a,&b) == 2)
printf("%u %u\n", a, b);
}
return 0;
}

I believe my earlier post gave better advice.
 
S

stathis gotsis

Ben Bacarisse said:
I think the following is more beginner-friendly:

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

int main(void)
{
unsigned long a, b;
FILE *pRead = fopen("test.dat", "r");

if (pRead == NULL)
printf("File cannot be opened\n");
else {
printf("Contents of test.dat : \n");

while (fscanf(pRead,"%u %u", &a,&b) == 2)
printf("%u %u\n", a, b);

You should consider closing the file here: fclose(pRead);
 
M

Micah Cowan

Ben Bacarisse said:
This is a dodgy pattern to recommend. You get, I think, undefined
behaviour if you test a NULL FILE * with feof (I get a core dump rather
than any flying demons -- YMMV). You definitely get UB if you test for
end-of-file before reading anything (because there may be no data to read)

Not true, as someone else pointed out elsethread. Not UB, simply useless.
I think the following is more beginner-friendly:

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

int main(void)
{
unsigned long a, b;
FILE *pRead = fopen("test.dat", "r");

if (pRead == NULL)
printf("File cannot be opened\n");
else {
printf("Contents of test.dat : \n");

while (fscanf(pRead,"%u %u", &a,&b) == 2)
printf("%u %u\n", a, b);
}
return 0;
}

Mm. This still has the flaw that if the file can't be opened, fscanf()
will attempt to read it anyway.

Also, while the above loop does terminate once the end of the file has
been reached, it will also terminate (silently) if a line is
encountered that doesn't have the expected format, or if a read error
occurs. At the very least, it's worth noting the latter.

It'd be worthwhile to declare an int (we'll call it nread), and save
the value from fscanf() so we can distinguish the cases:

while ((nread = fscanf(pRead, "%u %u", &a, &b)) == 2)
printf("%u %u\n", a, b);
if (ferror(pRead))
fputs("An error occurred while attempting to read test.dat.\n",
stderr);
else if (!feof(pRead))
fputs("A malformatted line was encountered, or a "
"number is too large.\n", stderr);

To polish this up a bit, it would be useful to keep a line-counter so
you can inform the user which line was malformatted.

Also, the program will of course accept input in the format:

1 12389 2 12788
3
4409 4
12489 5 2933 6
9280

It might be well to explicitly enforce that there are exactly two
numbers per line, if this is the expected format.
 
S

stathis gotsis

Micah Cowan said:
Mm. This still has the flaw that if the file can't be opened, fscanf()
will attempt to read it anyway.

scanf() is contained in the compound statement under "else", why will the
file be read if fopen fails?
while ((nread = fscanf(pRead, "%u %u", &a, &b)) == 2)
printf("%u %u\n", a, b);

I believe those format specifiers should be changed to "%lu" since a and b
are of type unsigned long.
 
B

Ben Bacarisse

Ben Bacarisse wrote:



True, as it probably internally dereferences NULL pointer.


This however I believe is not the case. The Standard says only this:
I see no requirement for any data to exist in the file (think empty
files), or any data to be read beforehand (think empty files again).

I know you can have empty data! I meant the program exhibited UB when
presented with an empty file because of what is does *after* testing for
eof. The test if fine, but it won't return true so scanf is called with
no data to read -- that was what I was trying to say with "... because
there may be no data to read". I should have been more explicit about the
UB comming from the read, not the eof test.
Here, you make the same mistake as both OP and Richard. Even if the
`fopen` fails you'll try to read from the file.

I don't think so. The fscanf is in the "else". It was the main point I
wanted to correct.
I believe my earlier post gave better advice.

I thought that feof would not return true unless an input operation had
failed. I may not be up on the latest standard so I will await correction
here, but if I am not mistaken your suggestion involved reading when there
might be nothing to read (and hence demons etc...).
 
B

Ben Bacarisse

You should consider closing the file here: fclose(pRead);

Absolutely, thank you. It was intened, after all, as an example for
beginners to copy so it should not be missing the fclose!
 
M

Micah Cowan

stathis gotsis said:
scanf() is contained in the compound statement under "else", why will the
file be read if fopen fails?

I wish you had retained that bit of context... in any event, yes,
you're right: I misread it.
I believe those format specifiers should be changed to "%lu" since a and b
are of type unsigned long.

Absolutely right: I had missed that as well.

-Micah
 
B

Ben Bacarisse

Not true, as someone else pointed out elsethread. Not UB, simply useless.

I am claiming that the original program exhibits UB because it tests eof
before the input operations. The UB is from doing fscanf on an empty file
or one which may not contain two numbers and then using the uninitialised
variable(s). I may not have been clear but I don't think I was wrong.

Mm. This still has the flaw that if the file can't be opened, fscanf()
will attempt to read it anyway.

<pantomime shouting>It's in the "else" part!</panto>

Excellent suggestions for enhancement snipped.
 
C

Chris Torek

I thought that feof would not return true unless an input operation had
failed.

This is correct. Not only must an input operation have failed,
it must have failed due to EOF. If it fails due to file-error
(corrupted file, unreadable CD-ROM, etc), you will get repeated
"EOF" indications from read operations, but feof(fp) will remain
false.

More commonly, bad input in a file will cause the scanf() engine
to jam up. Here, a loop that fails to test the fscanf() return
value will run forever:

some_loop_construct_here {
fscanf(fp, "%u%u", &a, &b);
/* fscanf() returns 0 because input stream contains, e.g., "fish" */

use(a, b);
/* use() gets previous values of a and b (or junk if there
are no previous values */
}

In any case, *any* time you see a "while" or "for" loop whose test
condition uses "feof", be suspicious. The loop is probably broken.
 
C

Chris Torek

It'd be worthwhile to declare an int (we'll call it nread), and save
the value from fscanf() so we can distinguish the cases:

while ((nread = fscanf(pRead, "%u %u", &a, &b)) == 2)
printf("%u %u\n", a, b);
if (ferror(pRead))
fputs("An error occurred while attempting to read test.dat.\n",
stderr);
else if (!feof(pRead))
fputs("A malformatted line was encountered, or a "
"number is too large.\n", stderr);

This code fragment does not use "nread" after exiting the loop.
To polish this up a bit, it would be useful to keep a line-counter so
you can inform the user which line was malformatted.

This is a good idea; however, doing so requires discarding the
scanf system, as "%u" includes an implicit "skip whitespace"
directive, and "whitespace" includes newlines. No amount of fiddling
with the scan format can repair this problem. The *only* solution
is to read input with something other than scanf (or, overly
complicated, scanf combined with something other than scanf, or
scanf with "%c" to read one character at a time, but these are just
silly ways of proving you can cut cheese with a "knife" made of
tofu).
 
B

Barry Schwarz

Okay, this if my first post so go easy on me plus I've only been using
C for a couple of weeks. I'm working on a program currently that
requires me to read data from a .dat file into a 2d array and then
print out the contents of the 2d array to the screen. I wil also need
to append data to the .dat file but mostly right now I'm worrying about
getting the info into the 2d array. My .dat file looks like this

1 20000
2 30000
3 40000

etc. The first number standing for an ID and the second standing for a
weight.

#include <stdio.h>
main()
{
FILE *pRead;
int array[20][3];

Why do you think the second dimension should be 3? There are only two
elements per line.
pRead = fopen("test.dat", "r");

if (pRead==NULL)
printf("\nFile cannot be opened\n");

else
printf("\nContents of test.dat\n\n");
fscanf(pRead,"%s,%s", array);

Multiple undefined behavior.

You have two format specifications. Each requires an
argument. You only have one argument.

%s requires a pointer to char. You provided an array name
which evaluates to a pointer to int.

Your format string says the values are separated by a comma.
There was no comma in your sample.

Since you want to read up to 20 pairs of ints, one method would be a
loop that repeatedly executes a statement like
fscanf(pread,"%d %d", &array[0], &array[1]);
while ( !feof(pRead) )

Read the faq for why this doesn't do what you want.
{
printf("%s%s\n", array);
fscanf(pRead, "%s,%s", array);

Why are you attempting to read more data when you have already
detected eof above?
}

}



I know I need to use a for loop or something to that idea so that I can
read in the column and then the row. I'm really drawing a blank here on
how to get that working. Could someone point me in the right direction?
Cause right now my output is coming out like this :

After undefined behavior, any output is possible.
11
20002000
22
30003000

etc.


Also, if I use %d instead of %s here, I get segmentation faults. Can
anyone explain why this is happening? Thanks.

Actually, segmentation faults are one of the more desirable forms of
undefined behavior. It eliminates any possible illusion that the code
is working properly.


Remove del for email
 
K

Keith Thompson

Chris Torek said:
but these are just
silly ways of proving you can cut cheese with a "knife" made of
tofu).

If you melt the cheese and dip the knife in liquid nitrogen.
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top