file.readalllines 最简单但不推荐大文件;streamreader 逐行读取更安全,内存占用恒定。

用 File.ReadAllLines 最简单但不推荐大文件
如果文件很小(比如几百行以内),File.ReadAllLines 是最直觉的做法:它把全部内容加载进内存,返回字符串数组,直接取索引即可。
但要注意:行号从 0 开始,第 N 行对应索引 N - 1;若 N 超出范围会抛 IndexOutOfRangeException。
- 示例:
string line = File.ReadAllLines("data.txt")[4];读第 5 行 - 必须加
try/catch或先检查Length - 10MB 以上文件可能引发
OutOfMemoryException,尤其在低内存环境
用 StreamReader 逐行跳过更安全
这是真正“只读第 N 行”的标准做法——不加载全文,只按需读取前 N 行,内存占用恒定(约几 KB)。
核心逻辑是调用 ReadLine() N 次,丢弃前 N−1 行,保留第 N 次返回值。
- 注意:
StreamReader.ReadLine()返回null表示已到文件末尾,需提前判断 - 别用
for (int i = 0; i 然后取最后一次结果——循环次数应为 <code>N,但读取动作要在循环内完成 - 示例片段:
string GetNthLine(string path, int n) { if (n < 1) throw new ArgumentException("行号从 1 开始"); using var sr = new StreamReader(path); for (int i = 1; i < n; i++) if (sr.ReadLine() == null) return null; // 提前结束 return sr.ReadLine(); }
用 File.ReadLines 看似简洁实则暗坑多
File.ReadLines 返回 IEnumerable<string></string>,支持 LINQ 的 Skip/Take,写起来像 File.ReadLines(p).Skip(n-1).FirstOrDefault(),但容易误用。
- 它不会立即读文件,而是延迟执行——看起来没开销,但每次枚举都重新打开文件(除非你缓存结果)
- 若反复调用该表达式,等于反复打开、逐行读到第 N 行,性能雪崩
- 不如直接用
StreamReader明确控制流,避免隐式行为 - 真要用 LINQ,至少包一层:
var lines = File.ReadLines(path).ToArray();——但这又回到内存全载的老路
Unicode、BOM 和换行符兼容性不能忽略
Windows 默认用 \r\n,Linux/macOS 用 \n,StreamReader 默认能识别;但若文件含 BOM(如 UTF-8 with BOM),ReadLine 不会吃掉它,首行内容开头可能带 \uFEFF。
- 打开时显式指定编码更稳妥:
new StreamReader(path, Encoding.UTF8) - 若需精确按字节偏移定位(极少数场景),得用
FileStream+ 自行解析换行符,但绝大多数情况没必要 - 行号定义本身依赖换行符——某行末尾缺
\n不影响计数,最后一行仍算一行
实际项目里,95% 的需求用 StreamReader 循环跳过就足够稳;只有确认文件永远小且简单时,才考虑 ReadAllLines。别被 LINQ 表面简洁骗了,延迟执行的副作用在日志分析或配置热加载里特别容易暴露。










