0

I'm writing a program in C, that is supposed to read arrays of doubles from files of arbitrary length. How to stop it at EOF? I've tried using feof, but then read the discussions:

Why is “while ( !feof (file) )” always wrong?

How does C handle EOF?

Then again they concern strings, not numbers. So when reading a file of the form "4 23.3 2 1.2", how to state the loop condition, so that my array A has all the numbers from the file and nothing more (also the iterator N = length of A)?

Here's the part of my code with feof, which produces one entry too many in A. The reallocation part is based on http://www.cplusplus.com/reference/cstdlib/realloc/ example.

int N = 0;    

double *A = NULL;
double *more_space;
double buff = 0;

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

while(feof(data) == 0) {
    fscanf(data, "%lg", &buff);
    more_space = realloc(A, (N+1)*sizeof(double));
    A = more_space;
    A[N] = buff;
    N++;
}
Community
  • 1
  • 1
Lurco
  • 199
  • 1
  • 2
  • 12
  • try reading a large buffer at a time , and than scanfing it... – vrdhn Jan 09 '14 at 13:46
  • 1
    `while(!feof(stream)) { ... } ` is wrong because of a logic problem, its not specific to strings or numbers. The logical pseudocode should be `while (there is more data) { do somewthing with the data }` When there is no more data, the loop terminates. Only at that point does it really make sense to check feof. For example, if your loop finished but you didn't make it to the end, maybe there is some invalid data in your file, or maybe a read error occured – Brandin Jan 09 '14 at 13:58

5 Answers5

1

Just stop when fscanf tells you there is no more data to be read. Its return value indicates the number of items it read or you get EOF if you reach end of file or there is a read error. Therefore, since you are reading only one item in each call, you will get 1 so long as reading is successful.

while (fscanf(data, "%lg", &buff) == 1) {
    ...
}

You may want to store the return value in a variable to verify whether reading stopped due to end-of-file or because the double parsing failed.

Lubomír Sedlář
  • 1,580
  • 2
  • 17
  • 26
  • 1
    fscanf returns EOF even if the end of the file has not yet been reached. For example if a matching error occurs or if the file can't be read for some other reason. – Brandin Jan 09 '14 at 14:03
1

The return value of fscanf is -1 when you hit eof (or other problems) so try while(fscanf(…) >= 0) for your outer loop. (Whether you accept a zero return value or not depends on how you want to handle bad data -- as other posters have pointed out, you will get 0 if no numbers are converted.

I like using the 'greater than or equals' test (instead of testing for ==1) because it doesn't break if you change the scanf format to, say, read two items from the input line. On the other hand, you could argue that it's better to verify that you always read exactly the correct number of arguments, and it's better to have your program 'fail fast' as soon as you change the number of scant arguments, than to have some mysterious failure later on, if you get bad input. It's a matter of style, and whether this is throwaway code for an assignment, or production code..

JVMATL
  • 2,064
  • 15
  • 25
  • All references say it returns EOF, assuming EOF has a particular value like -1 is wrong – Brandin Jan 09 '14 at 14:10
  • 1
    @Brandin true, but I'm not sure why you are appending this comment to my post: I didn't say to test against -1, I said to use a greater than 0 (or greater than 1) check. The standard *does* guarantee that whatever EOF is, it will be negative.. (and any other return values from fscanf are 0 or positive) – JVMATL Jan 09 '14 at 14:15
  • 1
    its related to the post. Rather than using >= x, personally I like to use an extra parameter in the format say "%f %f %f %c" and then make sure the return value is 3. That way you're sure there is no extra "garbage" after the data you are interested in. – Brandin Jan 09 '14 at 14:25
  • @brandin +1 interesting idea! (the extra %c) -- Now that I've learned something new for the day, I can go take a nap. :^) – JVMATL Jan 09 '14 at 14:28
1

During the scan, save the results of fscanf()

int cnt;
double d;
while ((cnt = fscanf("%f", &d)) == 1) {
  more_space = realloc(A, (N+1)*sizeof(double));
  if (more_space == NULL) Handle_MemoryError();
  A = more_space;
  A[N] = buff;
  N++;
}
if (cnt == 0) Handle_BadDataError();
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

You should do:

while (fscanf(data, "%lg", &buff) == 1)

That way if you hit a "non number" then it'll stop instead of loop infinitely!! ... And fscanf will check for EOF and it'll break the loop for you.

noelicus
  • 14,468
  • 3
  • 92
  • 111
0

Alternatively to other answers, read the entire file:

int32_t readFile ( char* filename, uint8_t** contentPtr, int32_t* sizePtr)
{
  int32_t ret = 0;
  struct stat st;
  FILE *file = NULL;
  uint8_t* content = NULL;

  memset ( &st, 0, sizeof ( struct stat ) );

  ret = stat( filename, &st );
  if( ret < 0 ) {
     return -1;
  }

  file = fopen64(filename, "rb");
  if ( file == NULL ) {
    return -1;
  }

  content = calloc ( st.st_size+10, sizeof ( uint8_t ) );

  ret = fread ( content, 1, st.st_size, file );
  fclose( file );
  if( ret != st.st_size ) {
    free(content);

    return -1;
  }

  *contentPtr = content;
  if (sizePtr != NULL) {
    *sizePtr = st.st_size;
  }

  return 0;
}

And then process the contents of the buffer with sscanf().

Dariusz
  • 21,561
  • 9
  • 74
  • 114