C:转到文件已知行的最佳方法

C : Best way to go to a known line of a file

我有一个文件,我想在其中迭代而不对当前行进行任何排序处理。我正在寻找的是转到文本文件的确定行的最佳方法。例如,在我到达预先确定的行之前,将当前行存储到一个变量中似乎是无用的。

示例:

file.txt

foo
fooo
fo
here

通常,为了获得 here,我会做类似的事情:

FILE* file = fopen("file.txt", "r");
if (file == NULL)
    perror("Error when opening file ");
char currentLine[100];
while(fgets(currentLine, 100, file))
{
    if(strstr(currentLine, "here") != NULL)
         return currentLine;
}

但是fgets将不得不无用地读取完整的三行并且currentLine将不得不存储foofooofo

知道 here 是第 4 行,还有更好的方法吗?类似于 go to 但对于文件?

因为你不知道每一行的长度,没有,你将不得不遍历前面的行。

如果你知道每行的长度,你可能会玩多少字节来移动文件指针。你可以用 fseek() 来做到这一点。

如果您不知道每行的长度,则必须全部检查一遍。但是,如果您知道要停止的行,则可以这样做:

while (!found && fgets(line, sizeof line, file) != NULL) /* read a line */
{
    if (count == lineNumber)
    {
         //you arrived at the line
         //in case of a return first close the file with "fclose(file);"
         found = true;
    }
    else
    {
        count++;
    }
}

至少你可以避免这么多电话strstr

您不能直接访问文本文件的给定行(除非所有行在 字节 中具有相同的大小;并且使用 UTF8 everywhere a Unicode character can take a variable number of bytes, 1 to 6; and in most cases lines have various length - different from one line to the next). So you cannot use fseek(因为您不事先不知道文件偏移量)。

但是(至少在 Linux 系统上),行以 \n(换行符)结尾。所以你可以逐字节读取并计算它们:

int c= EOF;
int linecount=1;
while ((c=fgetc(file)) != EOF) {
  if (c=='\n')
    linecount++;
}

这样就不需要存储整行了。

所以您可以通过这种方式(使用 while ((c=fgetc(file)) != EOF) && linecount<45) ...)到达第 45 行,然后才使用 fgets 或更好的 getline(3) on POSIX systems (see example). Notice that the implementation of fgets or of getline is likely to be built above fgetc, or at least share some code with it. Remember that <stdio.h> is buffered I/O, see setvbuf(3) 和相关函数读取整行。


另一种方法是分两次读取文件。第一遍存储偏移量(使用 ftell(3)...) of every line start in some efficient data structure (a vector, an hashtable, a tree...). A second pass use that data structure to retrieve the offset (of the line start), then use fseek(3)(使用该偏移量)。


第三种方法,POSIX 特定,将使用 mmap(2) into your virtual address space (this works well for not too huge files, e.g. of less than a few gigabytes). With care (you might need to mmap an extra ending page, to ensure the data is zero-byte terminated) you would then be able to use strchr(3)'\n'

来内存映射文件

在某些情况下,您可能会考虑 parsing your textual file line by line (using appropriately fgets, or -on Linux- getline, or generating your parser with flex and bison) and storing each line in a relational database (such as PostGreSQL or sqlite)。

PS。顺便说一句,行的概念(和行尾标记)从一个 OS 到下一个不等。在 Linux 上,行尾是一个 \n 字符。据传 Windows 行以 \r\n 结尾,等等...

C 中的 FILE *char 的流。在可搜索文件中,您可以使用带有 fseek() 的文件指针来寻址这些 char。但除此之外,文件中没有 "special characters",换行符只是另一个普通字符。

所以简而言之,不,你不能直接跳到文本文件的一行,只要你事先不知道行的长度。

C中的这个模型对应于典型操作系统提供的文件。如果您考虑一下,要知道各个行的起点,您的文件系统必须将此信息存储在某个地方。这意味着要特殊处理文本文件。

可以做的只是计算行数而不是模式匹配,像这样:

#include <stdio.h>

int main(void)
{
    char linebuf[1024];
    FILE *input = fopen("seekline.c", "r");
    int lineno = 0;
    char *line;
    while (line = fgets(linebuf, 1024, input))
    {
        ++lineno;
        if (lineno == 4)
        {
            fputs("4: ", stdout);
            fputs(line, stdout);
            break;
        }
    }
    fclose(input);
    return 0;
}