在 C# 开发中,逐行读取文本文件是一项非常基础且高频的文件操作能力。配置文件、日志记录、数据导出结果以及各类纯文本资料,通常都会以行为单位组织信息。逐行读取不仅可以让解析逻辑更清晰,也能避免在文件较大时一次性加载全部内容造成的内存压力。C# 在 System.IO 命名空间中提供了多种实现方式,常见的有 File.ReadAllLines、File.ReadLines 和 StreamReader.ReadLine。

C#逐行读取文本文件的核心思路
文本文件的本质是一串字符序列,而“行”是开发者根据换行符划分出来的逻辑单位。C# 读取文本文件时,底层通常会通过流来访问文件数据,再由读取器把字节转换为字符串,并按照换行规则切分内容。对于逐行读取来说,最关键的问题并不是单纯把文字读出来,而是如何在内存占用、读取性能、代码可读性和异常处理之间取得平衡。
从使用方式上看,File.ReadAllLines 更偏向便捷型 API,适合快速得到全部行数据;File.ReadLines 返回可枚举的字符串序列,适合以延迟加载的方式遍历文件;StreamReader 则提供更底层的读取能力,允许开发者自行控制每一次 ReadLine 调用。三种方式都能完成逐行读取,但适用场景并不完全相同。
除了读取方式之外,编码也是文本文件处理中不能忽视的问题。同一个文件在不同编码下可能呈现完全不同的字符结果。如果文件本身使用 UTF-8 编码,读取时指定 Encoding.UTF8 通常可以得到正确内容;如果文件来自历史系统,可能使用 GB2312 等其他编码,此时就需要显式指定对应编码,否则可能出现乱码。
常用实现方式与代码示例
在实际项目中,选择哪种方法主要取决于文件大小和处理目标。如果文件较小,并且后续需要频繁访问所有行,使用数组形式会更方便;如果文件较大,只需要按顺序处理每一行,延迟加载更稳妥;如果需要在读取过程中加入复杂判断、提前终止或自定义资源管理,则更适合使用流读取器。
下面分别用完整示例说明三种常见写法。阅读时可以重点关注两个问题:第一,代码是否足够简洁;第二,读取方式是否匹配文件大小与处理需求。很多文件读取问题并不是功能无法实现,而是方法选择不够合适,导致内存、性能或维护成本上升。
使用 File.ReadAllLines 快速读取小文件
File.ReadAllLines 是 C# 中最直观的按行读取方式之一。它会打开文件,读取全部内容,然后把每一行放入一个字符串数组中。对于体积较小的配置文件、模板文件或者测试数据文件,这种方式非常简单,代码量少,语义明确。
需要注意的是,File.ReadAllLines 会一次性把文件内容加载到内存中。如果文件只有几 KB 或几 MB,这种做法通常没有问题;但如果文件达到几百 MB 甚至更大,就可能造成明显的内存占用,甚至引发性能问题。因此,它更适合“文件不大,但希望快速拿到全部行”的场景。
using System;
using System.IO;
using System.Text;
class Program
{
static void Main()
{
string filePath = "test.txt";
try
{
// 按行读取文件内容到字符串数组
string[] lines = File.ReadAllLines(filePath, Encoding.UTF8);
// 遍历每一行内容
foreach (string line in lines)
{
Console.WriteLine(line);
}
}
catch (Exception ex)
{
Console.WriteLine("读取文件出错:" + ex.Message);
}
}
}
如果明确知道文件编码,建议使用带编码参数的重载方法。这样即使运行环境默认编码发生变化,也可以减少因编码不一致导致的乱码风险。
使用 File.ReadLines 以延迟加载方式读取大文件
File.ReadLines 同样按行读取文件,但它返回的是 IEnumerable<string>,不会立刻把所有行全部加载到内存中。遍历到哪一行,才会实际读取哪一行。这种延迟加载机制使它非常适合处理较大的文本文件。
这种方式常见的用法是在 foreach 中逐行消费数据。由于不会一次性生成完整的字符串数组,内存占用更可控。不过也要注意,在遍历过程中文件会保持打开状态,因此应尽快完成读取,不要在枚举过程中长时间持有文件句柄。
using System;
using System.IO;
using System.Text;
class Program
{
static void Main()
{
string filePath = "large_file.txt";
try
{
// 延迟加载逐行读取,适合逐行处理大文件
foreach (string line in File.ReadLines(filePath, Encoding.UTF8))
{
Console.WriteLine(line);
}
}
catch (Exception ex)
{
Console.WriteLine("读取文件出错:" + ex.Message);
}
}
}
使用 StreamReader 精细控制读取过程
StreamReader 是更底层的文本读取类。通过它的 ReadLine 方法,可以一行一行地读取内容。当读取到文件末尾时,ReadLine 会返回 null。这种方式让开发者可以精确控制读取节奏,也方便在循环中加入过滤、统计、解析或中断逻辑。
在使用 StreamReader 时,推荐配合 using 语句。这样无论读取过程是否正常结束,读取器占用的资源都可以被及时释放,避免文件句柄泄漏。对于大文件、日志文件或者需要边读取边处理的场景,StreamReader 通常是很稳妥的选择。
using System;
using System.IO;
using System.Text;
class Program
{
static void Main()
{
string filePath = "test.txt";
try
{
// 使用 using 语句自动释放 StreamReader 资源
using (StreamReader reader = new StreamReader(filePath, Encoding.UTF8))
{
string line;
// 循环读取每一行,读取到 null 表示文件结束
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
}
catch (Exception ex)
{
Console.WriteLine("读取文件出错:" + ex.Message);
}
}
}
方法选择、编码处理与常见问题
三种方法都可以完成逐行读取,但侧重点不同。选择时可以围绕文件大小、内存限制、是否需要完整数组以及是否需要精细控制来判断。下面的表格从内存占用、适用场景和灵活性三个维度进行对比。
| 方法 | 内存占用 | 适用场景 | 灵活性 |
|---|---|---|---|
File.ReadAllLines | 较高,一次性加载全部行 | 小文件,需要直接得到字符串数组 | 较低 |
File.ReadLines | 较低,延迟加载 | 大文件,只需要顺序遍历每一行 | 中等 |
StreamReader.ReadLine | 较低,逐行读取 | 大文件,需要自定义读取逻辑 | 较高 |
从表格可以看出,File.ReadAllLines 的优势是简单直接,但代价是内存占用更高;File.ReadLines 在保持代码简洁的同时改善了内存表现;StreamReader 则在灵活性和资源控制方面更有优势。实际开发中,如果无法提前判断文件大小,优先选择延迟加载或流式读取通常更安全。
编码不匹配导致乱码怎么办
读取文本文件时出现乱码,多数情况下是编码不匹配造成的。例如文件实际采用 GB2312 编码,而读取时按照 UTF-8 解码,就可能得到错误字符。解决这类问题的关键是先确认文件的真实编码,然后在读取时传入对应的编码参数。
如果文件来源明确,比如由某个系统导出,并且文档说明了编码格式,可以直接按说明指定编码。例如确认文件为 GB2312 编码时,可以通过 Encoding.GetEncoding("GB2312") 指定编码。如果文件来源不确定,可以结合文件生成方、历史系统约定或者样本测试来判断。对于中文文本文件,UTF-8 与 GB2312 是较常见的两种情况,读取前明确编码能显著减少乱码问题。
如何跳过空行与空白行
逐行读取时,文件中可能包含空行,也可能包含只由空格、制表符等空白字符组成的行。如果后续解析逻辑不关心这些行,可以在读取后进行判断。C# 中可以使用 string.IsNullOrWhiteSpace 来识别空字符串或仅包含空白字符的字符串。
using System;
using System.IO;
using System.Text;
class Program
{
static void Main()
{
string filePath = "test.txt";
try
{
using (StreamReader reader = new StreamReader(filePath, Encoding.UTF8))
{
string line;
while ((line = reader.ReadLine()) != null)
{
// 跳过空行和仅包含空白字符的行
if (!string.IsNullOrWhiteSpace(line))
{
Console.WriteLine(line);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("读取文件出错:" + ex.Message);
}
}
}
异常处理与资源释放不能省略
文件读取并不是一个绝对安全的过程。文件可能不存在,路径可能拼写错误,当前程序可能没有访问权限,磁盘也可能发生 IO 异常。因此,正式代码中通常需要使用 try catch 捕获异常,避免程序因为一次文件读取失败而直接崩溃。
资源释放同样重要。文件读取会占用操作系统文件句柄,如果读取器没有及时释放,可能导致文件被持续占用,影响后续写入、删除或移动操作。使用 using 语句是最简单的做法,它会在代码块结束时自动释放资源。如果没有使用 using,也应该确保在合适的时机调用 Dispose。
- 小文件优先追求开发效率时,可以选择
File.ReadAllLines。 - 大文件只需要顺序遍历时,优先选择
File.ReadLines。 - 需要自定义读取流程、提前中断或复杂过滤时,优先选择
StreamReader。 - 读取中文文本文件时,应重视编码设置,避免乱码。
- 正式项目中应加入异常处理,并确保文件资源被正确释放。
总体来说,C# 逐行读取文本文件并不复杂,但需要根据文件大小、处理方式和编码情况选择合适的方法。理解每种方法的内存特征与边界条件,可以让文件读取代码既简洁又稳健。在后续处理日志分析、配置读取或数据导入任务时,这些基础能力会经常派上用场。
C#逐行读取文件文本文件File_ReadAllLinesStreamReader修改时间:2026-07-01 05:54:30