如何用C语言高效打印文件中行号数组指定的内容?
高效打印文件中指定行号的方法
先看你提供的原始代码(这里有个明显问题:size=1但你访问了lineSeekArray[1],这会触发数组越界,属于未定义行为,得先修正这个问题):
const int size = 1; int lineSeekArray[size]; lineSeekArray[0] = 0; lineSeekArray[1] = 1; // 此处越界,size为1时索引仅能到0 static const char filename[] = "testfile.txt"; FILE *file = fopen ( filename, "r" ); int i =0; if ( file != NULL ) { char line [ 328 ]; /* 或其他合适的最大行长度 */ while ( fgets ( line, sizeof line, file ) != NULL ) {/* 读取一行 */ i++; if(i == 9) { fputs ( line, stdout ); /* 输出该行 */ } } fclose ( file ); }
你的需求是:根据用户输入的动态大小行号数组,打印数组中指定的行(比如数组包含0和1时打印这两行)。下面是两种高效的实现思路:
方案一:排序行号数组,顺序遍历匹配(推荐)
这种方法适配绝大多数场景,逻辑简单且性能优异:
- 先对目标行号数组做升序排序,这样我们可以按顺序遍历文件,匹配到目标行后就推进数组指针,直到所有目标都匹配完成,还能提前终止遍历。
- 遍历文件时,每读取一行就检查当前行号是否在目标数组中,匹配到就打印,同时标记该目标已完成。
优化后的代码示例:
#include <stdio.h> #include <stdlib.h> // qsort所需的整数比较函数 int compareInts(const void *a, const void *b) { return (*(int*)a - *(int*)b); } int main() { // 示例:用户输入的动态目标行号(这里假设要打印第0、1、5、9行) int targetLines[] = {9, 0, 1, 5}; int targetCount = sizeof(targetLines) / sizeof(targetLines[0]); // 排序目标行号,方便顺序匹配 qsort(targetLines, targetCount, sizeof(int), compareInts); static const char filename[] = "testfile.txt"; FILE *file = fopen(filename, "r"); if (!file) { perror("文件打开失败"); return 1; } char line[328]; int currentLine = 0; // 注意:如果你的行号从1开始计数,这里初始值改成1 int targetIndex = 0; // 遍历文件,直到读完或所有目标行都匹配完成 while (fgets(line, sizeof(line), file) != NULL && targetIndex < targetCount) { if (currentLine == targetLines[targetIndex]) { fputs(line, stdout); targetIndex++; // 匹配到一个目标,推进到下一个 } currentLine++; } fclose(file); return 0; }
为什么这个方案高效?
- 仅需遍历文件一次,无需反复打开或跳转。
- 排序后可提前终止遍历(所有目标匹配完成后直接退出循环),避免不必要的读取操作。
- 时间复杂度由文件读取(O(N),N为文件总行数)和排序(O(M log M),M为目标行数量)组成,在绝大多数场景下都足够高效。
方案二:预存行偏移量(适合多次查询场景)
如果需要多次查询不同的行号,可以先把文件每一行的起始字节偏移量存储起来,之后直接用fseek跳转读取:
#include <stdio.h> #include <stdlib.h> int main() { static const char filename[] = "testfile.txt"; FILE *file = fopen(filename, "r"); if (!file) { perror("文件打开失败"); return 1; } // 先遍历文件,记录每一行的起始偏移量 long long *lineOffsets = NULL; int lineCount = 0; long long currentOffset = ftell(file); char line[328]; while (fgets(line, sizeof(line), file) != NULL) { // 动态扩容偏移量数组 long long *temp = realloc(lineOffsets, (lineCount + 1) * sizeof(long long)); if (!temp) { perror("内存分配失败"); free(lineOffsets); fclose(file); return 1; } lineOffsets = temp; lineOffsets[lineCount] = currentOffset; lineCount++; currentOffset = ftell(file); } // 示例:用户输入的目标行号 int targetLines[] = {0, 1, 9}; int targetCount = sizeof(targetLines) / sizeof(targetLines[0]); // 跳转读取目标行 for (int i = 0; i < targetCount; i++) { int lineNum = targetLines[i]; if (lineNum < 0 || lineNum >= lineCount) { printf("行号%d不存在\n", lineNum); continue; } fseek(file, lineOffsets[lineNum], SEEK_SET); fgets(line, sizeof(line), file); fputs(line, stdout); } free(lineOffsets); fclose(file); return 0; }
注意事项:
- 该方案适合多次重复查询的场景,第一次遍历存储偏移量的开销可以被后续查询分摊。
- 文本文件的换行符(Windows是
\r\n,Linux是\n)不影响ftell记录的字节偏移,所以跳转读取是可靠的。 - 如果文件极大(比如几十GB),存储所有行偏移量会占用较多内存,此时方案一更合适。
额外提醒
- 行号计数一致性:你的原始代码中
i从0开始,i++后判断i==9,实际打印的是第9行(行号从1开始)还是第10行?一定要明确行号的计数起点,避免逻辑错误。 - 动态数组处理:如果用户输入的行号数量是动态的,要用
malloc或realloc分配数组,不能用固定大小的数组。 - 边界检查:要判断目标行号是否在文件的有效行号范围内,避免越界访问。
内容的提问来源于stack exchange,提问作者Samun
相关产品推荐
相关产品推荐

