在 C 中使用 fscanf 扫描文本文件中的特定行?

Posted

技术标签:

【中文标题】在 C 中使用 fscanf 扫描文本文件中的特定行?【英文标题】:Scan a particular line in a text-file with fscanf in C? 【发布时间】:2022-01-14 08:54:21 【问题描述】:

我想用 fscanf 在文本文件中进行搜索。 这是我正在搜索的数字,这些数字按值排序。 因此,我想做一个二分查找,从中间开始等等。 我的想法是先计算行数,除以二找到中间。 但是如何告诉 fscanf 看那行呢?

int seach_textfile(struct data *ean13, unsigned long long int *input)

    FILE *fp_read = fopen("data.tex", "r");
    unsigned long long int read, n_lines;

    // count lines
    n_lines = 0;
    while (fscanf(fp_read,"\n") == 1)
    
        n_lines++;
    

    int n_line_low = 0;
    int n_line_mid;
    int n_line_high =n_lines;

    while(n_line_high>n_line_low)
    
        n_line_mid; = (n_line_low + n_line_high)/2;
        fscanf(fp_read,"%lld \n", read).... at n_line_mid  // <----here!! (only to read first entr on line)
        if(*input > read)
        
            n_line_low = n_line_mid +1;
        
        else
        
            n_line_high = n_line_mid;
        
     
    if(*input == read)
    
        fscanf(fp_read,"%lld %s %s %s",*ean13->ean,*ean13->country,*ean13->manufacture,*ean13->product).... at n_line_mid  // <----here!!
        return 1;
    
    else return 0;

【问题讨论】:

while (fscanf(fp_read,"\n") == 1) 不计算 fscanf(fp_read,"\n") 读取并使用所有前导空白。 您可以使用 fgets 逐行读取,对于读取的每一行(进入提供给 fgets 的缓冲区),使用 sscanf 在行中搜索。并测试函数的返回。 如果您愿意阅读整个文件以计算行数,为什么不一边阅读一边寻找信息?在二分搜索操作期间,您将如何避免读取所有行直到您要搜索的行号?请记住,行的长度变化很大。 除非您可以有效地移动到随机行的开头,否则您无法有效地对文件执行二进制搜索。这需要固定长度的行或索引。如果你有其中任何一个,那么你不需要读取文件来计算行数。如果您没有其中任何一个,那么简单地执行文件的线性扫描是最简单和最有效的,因为无论如何您都必须这样做才能计算行数或建立索引。 数字,作为文本,在文件中的间距不均匀。也许找到文件字节长度,然后应用二进制搜索。长度的每一半可能指向一行的中间,然后向后(或向前)搜索一行的开头,然后读取数字。我没有看到一个高度便携的解决方案来读取文本文件并通过二进制搜索查找是 UB。相反,可以读取记录每行开始在数组中的偏移量的文件,然后对该数组进行二进制搜索。祝你好运。 【参考方案1】:

我会先阅读所有行并将它们存储在链接列表中。然后你可以做任何你想做的事。如果你往回走,你也可以添加prev指针。

typedef struct line

    struct line *next;
    char line[];
line;


#define MAXLINE (8*1024)


line *readFile(FILE *fi, int removeLF)

    char *workbuff = malloc(MAXLINE);
    line *head = NULL, **current = &head;
    if(fi && workbuff)
    
        while(fgets(workbuff, MAXLINE, fi))
        
            size_t len = strlen(workbuff);
            *current = malloc(sizeof(**current) + len + 1);
            if(current)
            
                memcpy(current[0] -> line, workbuff, len + 1);
                if(removeLF && current[0] -> line[len - 1] == '\n') current[0] -> line[len - 1] = 0;
                current = &current[0] -> next;
            
        
    
    *current = NULL;
    return head;


void print(line *lines)

    size_t cline = 0;
    do
    
        printf("line no %zu = `%s`\n", ++cline, lines -> line);
        lines = lines -> next;
    while(lines);


  
int main(void)

    line *file = readFile(stdin, 1);
    print(file);

【讨论】:

当然,但是如果您阅读了所有行,那么您可以通过该过程确定所需项目是否存在,如果存在则在哪一行。如果这些是唯一要回答的问题,那么以任何形式将文件内容存储在内存中都是没有意义的。【参考方案2】:

OP 的代码无法找到行数,因为 fscanf(fp_read,"\n") 仅读取前导空白并在第一个空白处阻塞。

无论如何,行数并不是那么有用,因为它只是搜索以找到要寻找的大致位置。文件长度也可以。


替代方案

    二进制模式打开文本文件

    快速确定其长度。 (例如,重复的 fread()s 或 fstat()(如果有)的总和。)

    left = 0; right = length-1

    mid = (left+right)/2 使用fseek(mid)

    fgetc()fgets() 到行尾:例如寻找'\n'EOF

    读取号码。如果匹配,我们就完成了。

    如果太大,right = mid - 1。如果太小,left = ftell()

    如果是right &gt;= left,则循环回到第4步。

    失败 - 找不到号码。

一路检查 I/O 函数返回值。

如果代码需要查找另一个数字,请省略第 2 步,因为文件长度已知。

【讨论】:

所以我使用 EOF,以某种方式获取读取位置...返回一半...然后转到行首...这可能吗? @Too_Old_To_Get_IT 没有简单或预定义的方法来“转到行首”。搜索后,您可能必须阅读一个字符块——我,我会使用fread——然后在其中搜索标记行边界的\n。您必须处理您读取的块包含零个、一个或多个行边界的情况。这很棘手,并且有很多情况需要做对,但有可能让它工作,并且可以很好地工作。但这不是一个容易的问题。 @Too_Old_To_Get_IT "....然后转到行首...这可能吗?"是的,可能。我认为下一行更容易。

以上是关于在 C 中使用 fscanf 扫描文本文件中的特定行?的主要内容,如果未能解决你的问题,请参考以下文章

从C中的文本文件读取int值

C语言中fscanf()按一定的格式读取文本文件!

如何从c中的txt文件正确扫描内容

使用scanner类更改文本文件中的特定文本(java)

fscanf()函数。使用C中的模式读取文件

C中的fscanf - 如何确定逗号?