Erol Guzoğlu
Erol Guzoğlu

Reputation: 486

How can I read lines from bottom up using C?

I need to read numbers which are listed in a file from bottom up. How can I do that using C?

The file is like:

4.32
5.32
1.234
0.123
9.3
6.56
8.77

For example, I want to read the last three numbers. They have to be float type.

8.77
6.56
9.3

PS.: Actually I need a solution which is playing with the file pointer position using fseek, etc.

Upvotes: 14

Views: 3026

Answers (5)

R Sahu
R Sahu

Reputation: 206607

The notion of reading backwards from a file does not exist.

One solution is to read all the numbers and store only the last three read.

float numbers[3];
char line[100]; // Make it large enough
int = 0;
char* end;
for ( ; ; ++i )
{
    i %= 3; // Make it modulo 3.
    if ( fgets(line, 100, stdin) == NULL )
    {
       // No more input.
       break;
    }

    float n = strtof(line, &end);
    if ( line == end )
    {
       // Problem converting the string to a float.
       // Deal with error
       break;
    }

    if ( errno == ERANGE )
    {
       // Problem converting the string to a float within range.
       // Deal with error
       break;
    }

    numbers[i] = n;
}

If there are at least three numbers in the file, the last three numbers are numbers[i], numbers[(i+2)%3], and numbes[(i+1)%3].

Upvotes: 3

Erol Guzoğlu
Erol Guzoğlu

Reputation: 486

I solved my problem with the following code. I read the second half of the file.

  FILE *fp = fopen("sample.txt","r");

  if( fp == NULL )
  {
    perror("Error while opening the file.\n");
    exit(EXIT_FAILURE);
  }

  int size=0;
  char ch;

  //Count lines of file
  while(( ch = fgetc(fp) ) != EOF )
  {
    if (ch=='\n') { size++; }
  }

  int i;
  float value;

  //Move the pointer to the end of the file and calculate the size of the file.
  fseek(fp, 0, SEEK_END);
  int size_of_file = ftell(fp);

  for (i=1; i<=size/2; i++)
  {
    //Set pointer to previous line for each i value.
    fseek(fp, (size_of_file-1)-i*5, SEEK_SET);
    fscanf(fp, "%f", &value);
  }

Upvotes: 0

Joshua Byer
Joshua Byer

Reputation: 519

Well, the obvious way is to read them all, put them into an array and then get the last three.

Upvotes: 6

rodrigovr
rodrigovr

Reputation: 454

First, open the file:

FILE* fp = fopen(..., "r");

Then, skip to EOF:

fseek(fp, 0, SEEK_END);

Now, go back X lines:

int l = X, ofs = 1;
while (l && fseek(fp, ofs++, SEEK_END) == 0) {
    if (fgetc(fp) == '\n' && ofs > 2) l--;
}

And finally, read X numbers from the current position:

float numbers[X];
for(int p = 0; p < X; p++) fscanf(fp, "%f", &numbers[p];

Upvotes: 1

zwol
zwol

Reputation: 140589

It's important to understand that no modern operating system tracks the position of line breaks within a file. (VMS could, and I'm pretty sure so could some IBM mainframe operating systems, but you're probably not using any of those.) So it's not possible to seek to a line boundary. It is also not possible to read byte-by-byte in reverse order.

Therefore, the simplest way to read the last three numbers in the file, in reverse, is to read the entire file in forward order, keeping the most recently seen three numbers in a buffer. When you hit EOF, just process that buffer backward.

A more efficient, but significantly more complicated, technique is to guess a position close to but before the last three numbers in the file; seek to that position, then discard characters till you hit a line break; and use the technique in the previous paragraph from that point. If you guessed wrong and the buffer winds up with fewer than three numbers in it, guess again.

And a third approach would be to use fseek (with SEEK_END) and fread to read the last 1024 or so bytes of the file, set a pointer to the end of the block, and parse it backward. This would be quite efficient, but would have even more headache-inducing corner cases to get right than the previous suggestion. (What exactly do you do if the last three lines of the file, collectively, are more than 1024 bytes long?)

FYI, the correct way to read floating-point numbers in C is to use fgets and strtod. DO NOT use either atof or scanf for this; atof doesn't tell you about syntax errors, and scanf triggers undefined behavior on overflow.

P.S. If you have the shell utility tac (which is a GNUism), the easiest option of all would be to write your program to process the first three numbers on standard input, and then invoke it as tac < input.file | ./a.out. Skimming the code leads me to believe that tac implements my "third approach", with some additional cleverness.

Upvotes: 27

Related Questions