ZetCode

C# TextReader

最后修改于 2025 年 4 月 20 日

本教程讲解如何在 C# 中使用 TextReader 类从各种来源读取文本数据。TextReader 是一个抽象类,它提供了读取字符流的方法。

TextReader 类是 StreamReader 和 StringReader 的基类。 它提供了一个方便的 API,用于从不同的输入源读取文本数据。

TextReader 旨在读取字符输入,而不是二进制数据。 当与它的具体实现一起使用时,它会自动处理文本编码。

基本 TextReader 示例

此示例演示如何使用从 TextReader 继承的 StreamReader 从文件中读取文本。 我们逐行读取文本文件。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        // Create a sample text file first
        File.WriteAllText("example.txt", "First line\nSecond line\nThird line");

        // Read using TextReader (StreamReader)
        using (TextReader reader = new StreamReader("example.txt"))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                Console.WriteLine(line);
            }
        }
    }
}

该示例创建一个包含三行的文本文件,然后使用 StreamReader 将其读回。 当到达文件末尾时,ReadLine 方法返回 null。 这种模式通常用于逐行处理文本文件。

using 语句确保正确清理资源。 StreamReader 会根据文件的字节顺序标记 (BOM) 自动处理文件编码检测(如果存在)。

读取整个文件内容

TextReader 提供了立即读取整个内容的方法。 此示例显示如何将整个文件读入字符串。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        // Create a sample text file
        File.WriteAllText("document.txt", "This is the complete content\nof our text file.");

        // Read entire content
        using (TextReader reader = new StreamReader("document.txt"))
        {
            string content = reader.ReadToEnd();
            Console.WriteLine("File content:");
            Console.WriteLine(content);
        }
    }
}

ReadToEnd 方法读取从当前位置到文本流末尾的所有字符。 这对于内存使用量不是问题的小文件很有用。

对于大文件,请考虑逐行或分块读取,以避免高内存消耗。 该示例显示了在适当情况下读取完整文件内容的简单性。

单独读取字符

TextReader 允许读取单个字符或字符块。 此示例演示了逐字符读取。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        File.WriteAllText("chars.txt", "ABC");

        using (TextReader reader = new StreamReader("chars.txt"))
        {
            int charCode;
            while ((charCode = reader.Read()) != -1)
            {
                char c = (char)charCode;
                Console.WriteLine($"Read character: {c} ({(int)c})");
            }
        }
    }
}

Read 方法将下一个字符作为整数返回,或者在流结尾返回 -1。 我们将整数转换为 char 以进行显示。 这种低级别的方法提供了对文本处理的精确控制。

逐字符读取对于解析任务或处理具有特定格式要求的文本非常有用。 该示例同时显示了字符及其 Unicode 代码点。

读取字符块

为了更好地处理大文件,TextReader 可以读取字符块。 此示例一次读取 10 个字符的块。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        File.WriteAllText("lorem.txt", "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");

        using (TextReader reader = new StreamReader("lorem.txt"))
        {
            char[] buffer = new char[10];
            int charsRead;
            
            while ((charsRead = reader.ReadBlock(buffer, 0, buffer.Length)) > 0)
            {
                string chunk = new string(buffer, 0, charsRead);
                Console.WriteLine($"Read {charsRead} chars: {chunk}");
            }
        }
    }
}

ReadBlock 读取最多指定数量的字符,并返回实际读取的计数。 该缓冲区被重用于每个读取操作。 这种方法对于大型文件来说是节省内存的。

该示例显示了如何每次处理读取的准确字符数。 可以根据性能要求和典型文件大小调整缓冲区大小。

使用 StringReader

StringReader 是另一个 TextReader 实现,它从字符串读取。 此示例演示了从内存中字符串读取。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        string text = "Line 1\nLine 2\nLine 3";
        
        using (TextReader reader = new StringReader(text))
        {
            string line;
            int lineNumber = 1;
            
            while ((line = reader.ReadLine()) != null)
            {
                Console.WriteLine($"Line {lineNumber}: {line}");
                lineNumber++;
            }
        }
    }
}

StringReader 提供了与 StreamReader 相同的接口,但处理的是字符串而不是文件。 这对于测试或处理内存中的文本数据很有用。

该示例显示了当文本源更改时,StringReader 如何与 StreamReader 互换使用。 相同的读取模式适用于这两种实现。

查看下一个字符

TextReader 的 Peek 方法允许查看下一个字符而不使用它。 此示例演示了查看。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        File.WriteAllText("peek.txt", "ABC");

        using (TextReader reader = new StreamReader("peek.txt"))
        {
            // Peek at first character
            int firstChar = reader.Peek();
            Console.WriteLine($"First character will be: {(char)firstChar}");
            
            // Now read it
            int readChar = reader.Read();
            Console.WriteLine($"Actually read: {(char)readChar}");
            
            // Verify they match
            Console.WriteLine($"Peeked and read match: {firstChar == readChar}");
        }
    }
}

Peek 返回下一个字符而不推进位置。 它在流结尾返回 -1,就像 Read 一样。 这对于前瞻性解析很有用。

该示例显示了查看如何允许在决定如何处理下一个字符之前检查它。 查看的字符仍然可用于后续读取操作。

将 TextReader 与其他 API 结合使用

TextReader 与其他 .NET API 集成良好。 此示例将 TextReader 与 LINQ 结合使用来处理行。

Program.cs
using System;
using System.IO;
using System.Linq;

class Program
{
    static void Main()
    {
        File.WriteAllText("data.txt", "10\n20\n30\n40\n50");

        using (TextReader reader = new StreamReader("data.txt"))
        {
            var numbers = reader.ReadToEnd()
                .Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
                .Select(int.Parse)
                .Where(n =>  n > 25)
                .OrderByDescending(n =>  n);
            
            Console.WriteLine("Numbers > 25:");
            foreach (var num in numbers)
            {
                Console.WriteLine(num);
            }
        }
    }
}

该示例读取所有内容,分割成行,转换为整数,过滤和排序。 这显示了 TextReader 与 LINQ 操作的兼容性,可实现强大的文本处理。

对于非常大的文件,请考虑在带有 LINQ 惰性求值的循环中使用 ReadLine,而不是 ReadToEnd。 该方法取决于文件大小和处理需求。

来源

TextReader 类文档

本教程介绍了在 C# 中使用 TextReader 读取文本数据,包括逐行读取、完整内容读取和字符处理。 我们探讨了 StreamReader 和 StringReader 实现。

作者

我叫 Jan Bodnar,是一名热情的程序员,拥有丰富的编程经验。 我自 2007 年以来一直在撰写编程文章。 迄今为止,我已经撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 C# 教程