简体   繁体   中英

Reading a specific line from a file in C

Okay, so after reading both: How to read a specific line in a text file in C (integers) and What is the easiest way to count the newlines in an ASCII file? I figured that I could use the points mentioned in both to both efficiently and quickly read a single line from a file .

Here's the code I have:

char buf[BUFSIZ];
intmax_t lines = 2; // when set to zero, reads two extra lines.
FILE *fp = fopen(filename, "r");
while ((fscanf(fp, "%*[^\n]"), fscanf(fp, "%*c")) != EOF)
{

  /* globals.lines_to_feed__queue is the line that we _do_ want to print,
     that is we want to ignore all lines up to that point:
     feeding them into "nothingness" */

  if (lines == globals.lines_to_feed__queue) 
  {
    fgets(buf, sizeof buf, fp);
  }
  ++lines;
}
fprintf(stdout, "%s", buf);
fclose(fp);

Now the above code works wonderfully, and I'm extrememly pleased with myself for figuring out that you can fscanf a file up to a certain point, and then use fgets to read whatever data is at said point into a buffer, instead of having to fgets every single line and then fprintf the buf, when all I care about is the line that I'm printing: I don't want to be storing strings that I could care less about in a buffer that I'm only going to use once for a single line .

However, the only issue I've run into, as noted by the // when set to zero, reads two extra lines comment: when lines is initialized with a value of 0 , and the line I want is like 200 , the line I'll get will actually be line 202 . Could someone please explain what I'm doing wrong here/why this is happening and whether my quick fix lines = 2; is fine or if it is insufficient (as in, is something really wrong going on here, and it just happens to work?)

There are two reasons why you have to set the lines to 2 , and both can be derived from the special case where you want the first line.

On one hand, in the while loop the first thing you do is use fscanf to consume a line, then you check if the lines counter matches the line you want. The thing is that if the line you want is the one you just consumed you are out of luck. On the other hand you are basically moving through lines by finding the next \\n and incrementing lines after you check if the current line is the one you're after.

These two factors combined cause the offset in the lines count, so the following is a version of the same function taking them into account. Additionally it also contains a break; statement once you get to the line you are looking for, so that the while loop stops looking further into the file.

void read_and_print_line(char * filename, int line) {
  char buf[BUFFERSIZE];
  int lines = 0;
  FILE *fp = fopen(filename, "r");
  do 
  {
    if (++lines == line) {
      fgets(buf, sizeof buf, fp);
      break;
    }

  }while((fscanf(fp, "%*[^\n]"), fscanf(fp, "%*c")) != EOF);
  if(lines == line)
    printf("%s", buf);
  fclose(fp);
}

Just as another way of looking at the problem… Assuming that your global specifies 1 when the first line is to be printed, 2 for the second, etc, then:

char buf[BUFSIZ];
FILE *fp = fopen(filename, "r");
if (fp == 0)
    return;  // Error exit — report error.

for (int lineno = 1; lineno < globals.lines_to_feed_queue; lineno++)
{
    fscanf(fp, "%*[^\n]");
    if (fscanf(fp, "%*c") == EOF)
        break;
}

if (fgets(buf, sizeof(buf), fp) != 0)
    fprintf(stdout, "%s", buf);
else
    …requested line not present in file…
fclose(fp);

You could replace the break with fclose(fp); and return; if that's appropriate (but do make sure you close the file before exiting; otherwise, you leak resources).

If your line numbers are counted from 0, then change the lower limit of the for loop to 0 .

First, about what is wrong here: this code is unable to read the very first line in the file (what happens if globals.lines_to_feed__queue is 0?). It would also miscount lines shall the file contain successive newlines.

Second, you must realize that there is no magic. Since you don't know at which offset the string in question lives, you have to patiently read file character by character, counting end-of-strings along the way. It doesn't matter if you delegate the reading/counting to fgets / fscanf , or fgetc each character for manual inspection - either way an uninteresting piece of file will make its way from the disk into the OS buffers, and then into the userspace for interpretation.

Your gut feeling is absolutely correct: the code is broken.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM