ZetCode

C# TextWriter

最后修改于 2025 年 4 月 20 日

本教程介绍了如何在 C# 中使用 TextWriter 类将文本数据写入文件和流。 TextWriter 是一个抽象类,提供了顺序写入文本的方法。

TextWriter 类是所有写入文本的类的基类。 它处理具有特定编码的字符输出到流。 常见的派生类包括 StreamWriter 和 StringWriter。

TextWriter 提供了将字符串、字符和原始数据类型作为文本写入的方法。 它支持异步操作,可以与各种文本格式和编码一起使用。

基本 TextWriter 示例

此示例演示了如何使用 StreamWriter(继承自 TextWriter)将文本写入文件。 我们创建一个文件并写入几行。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        using (TextWriter writer = new StreamWriter("output.txt"))
        {
            writer.WriteLine("This is the first line.");
            writer.WriteLine("Writing numbers: {0}, {1}", 42, 3.14);
            writer.Write("This will be ");
            writer.WriteLine("on the same line.");
        }
        
        Console.WriteLine("Text written to output.txt");
    }
}

该示例创建一个 StreamWriter 实例来写入 output.txt。 WriteLine 添加带有换行符的文本,而 Write 在没有换行符的情况下写入。 using 语句确保正确清理资源。

StreamWriter 自动处理文件创建和编码(默认为 UTF-8)。 该示例演示了带有占位符的格式化输出和简单的字符串写入。 该文件将包含三行文本,并带有正确的行尾符。

使用 StringWriter 写入字符串

StringWriter 是另一种 TextWriter 实现,它写入 StringBuilder。 此示例展示了如何在内存中捕获文本。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        using (StringWriter writer = new StringWriter())
        {
            writer.WriteLine("Current date: {0:d}", DateTime.Now);
            writer.WriteLine("Current time: {0:t}", DateTime.Now);
            writer.WriteLine("Temperature: {0}°C", 23.5);
            
            string result = writer.ToString();
            Console.WriteLine("Captured text:\n" + result);
        }
    }
}

StringWriter 将所有写入的文本收集在内存中。 ToString 方法检索完整的字符串。 这对于构建具有多个写入操作的字符串非常有用。

该示例演示了格式化的日期/时间输出和数值。 当您需要在使用复杂字符串之前构建它们(例如,用于日志记录或报表生成)时,StringWriter 尤其有价值。

指定文本编码

TextWriter 允许在写入文件时指定字符编码。 此示例展示了如何使用不同的编码写入文本。

Program.cs
using System;
using System.IO;
using System.Text;

class Program
{
    static void Main()
    {
        // Write with UTF-8 encoding (default)
        using (TextWriter writer1 = new StreamWriter("utf8.txt", false, Encoding.UTF8))
        {
            writer1.WriteLine("UTF-8 encoded text: café");
        }

        // Write with Unicode (UTF-16) encoding
        using (TextWriter writer2 = new StreamWriter("unicode.txt", false, Encoding.Unicode))
        {
            writer2.WriteLine("Unicode encoded text: café");
        }

        // Write with ASCII encoding (characters outside ASCII will be replaced)
        using (TextWriter writer3 = new StreamWriter("ascii.txt", false, Encoding.ASCII))
        {
            writer3.WriteLine("ASCII encoded text: café");
        }
        
        Console.WriteLine("Files created with different encodings");
    }
}

不同的编码以不同的方式处理特殊字符。 UTF-8 是最常见的编码,支持所有 Unicode 字符。 ASCII 仅支持基本的英文字符。

该示例创建了三个文件,它们具有相同的文本但编码不同。 café 文本在 UTF-8 和 Unicode 文件中将正确显示,但在 ASCII 中将丢失 é 字符。 写入文本文件时,始终考虑您的编码要求。

异步写入

TextWriter 支持异步操作。 此示例演示了如何异步地将文本写入文件。

Program.cs
using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        using (TextWriter writer = new StreamWriter("async.txt"))
        {
            await writer.WriteLineAsync("First line written asynchronously");
            await writer.WriteAsync("This is ");
            await writer.WriteLineAsync("combined asynchronously");
            
            char[] chars = { 'A', 's', 'y', 'n', 'c', ' ', 'c', 'h', 'a', 'r', 's' };
            await writer.WriteAsync(chars, 0, chars.Length);
        }
        
        Console.WriteLine("Asynchronous writing completed");
    }
}

WriteLineAsyncWriteAsync 这样的 Async 方法不会阻塞调用线程。 这对于执行文件 I/O 的响应式应用程序非常重要。

该示例展示了三种异步写入的方式:写入完整的行,写入部分字符串,以及写入字符数组。 async 方法遵循与其同步对应方法相同的模式,但返回 Tasks。

写入格式化数据

TextWriter 提供了写入格式化数据的方法。 此示例展示了各种格式化选项。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        using (TextWriter writer = new StreamWriter("formatted.txt"))
        {
            // Basic string formatting
            writer.WriteLine("Formatted values: {0}, {1}, {2}", 10, 20.5, "text");
            
            // Number formatting
            writer.WriteLine("Currency: {0:C}", 125.8);
            writer.WriteLine("Decimal: {0:D5}", 42);
            writer.WriteLine("Fixed point: {0:F2}", 3.14159);
            
            // Date formatting
            DateTime now = DateTime.Now;
            writer.WriteLine("Short date: {0:d}", now);
            writer.WriteLine("Long date: {0:D}", now);
            writer.WriteLine("Custom format: {0:yyyy-MM-dd HH:mm:ss}", now);
        }
        
        Console.WriteLine("Formatted data written to file");
    }
}

WriteLine 方法支持带有占位符的复合格式。 冒号后的格式字符串指定应如何显示值。

该示例演示了数值格式(货币、十进制、定点)和日期/时间格式(短格式、长格式、自定义格式)。 格式字符串提供了对文本输出外观的精确控制。 这对于生成报告或结构化输出文件尤其有用。

写入多个 Writer

您可以同时写入多个 TextWriter 实例。 此示例展示了如何同时写入控制台和文件。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        using (TextWriter fileWriter = new StreamWriter("multioutput.txt"))
        using (TextWriter consoleWriter = Console.Out)
        {
            // Create a writer that writes to both destinations
            using (TextWriter multiWriter = TextWriter.Synchronized(
                new MultiTextWriter(fileWriter, consoleWriter)))
            {
                multiWriter.WriteLine("This will appear in both places");
                multiWriter.WriteLine("Current time: {0:T}", DateTime.Now);
                multiWriter.WriteLine("Value: {0}", 42 * 1.5);
            }
        }
        
        Console.WriteLine("Check multioutput.txt for the same content");
    }
}

class MultiTextWriter : TextWriter
{
    private readonly TextWriter[] writers;
    
    public MultiTextWriter(params TextWriter[] writers)
    {
        this.writers = writers;
    }
    
    public override Encoding Encoding =>  Encoding.UTF8;
    
    public override void Write(char value)
    {
        foreach (var writer in writers)
            writer.Write(value);
    }
    
    public override void Write(string value)
    {
        foreach (var writer in writers)
            writer.Write(value);
    }
    
    public override void Flush()
    {
        foreach (var writer in writers)
            writer.Flush();
    }
    
    public override void Close()
    {
        foreach (var writer in writers)
            writer.Close();
    }
}

自定义的 MultiTextWriter 类将所有写入转发到多个 TextWriter 实例。 TextWriter.Synchronized 确保线程安全的操作。

此技术对于您希望输出同时发送到控制台和文件的日志记录方案非常有用。 该示例写入三行,它们在两个目标中看起来相同。 自定义 writer 通过委托给包含的 writers 来处理所有基本的写入操作。

释放和刷新

在使用 TextWriter 时,正确的资源管理至关重要。 此示例演示了刷新和释放的重要性。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        // Example 1: Forgetting to flush/dispose
        TextWriter writer1 = new StreamWriter("flush1.txt");
        writer1.Write("This might not appear in the file");
        // Missing flush/dispose - content may be lost
        
        // Example 2: Manual flush
        TextWriter writer2 = new StreamWriter("flush2.txt");
        writer2.Write("This will appear because we flush");
        writer2.Flush(); // Ensures data is written to disk
        writer2.Close();
        
        // Example 3: Using statement (recommended)
        using (TextWriter writer3 = new StreamWriter("flush3.txt"))
        {
            writer3.Write("This will appear thanks to using");
        } // Automatically calls Dispose which calls Flush
        
        Console.WriteLine("Check the files to see which content appears");
    }
}

TextWriter 默认情况下缓冲输出。 Flush 立即写入缓冲的数据。 Dispose(由 using 调用)刷新并释放资源。

该示例展示了具有不同结果的三种方法。 第一种可能会丢失数据,因为既没有调用 flush 也没有调用 dispose。 第二种显式刷新和关闭。 第三种(推荐)使用 using 语句进行自动资源清理。 始终正确管理 writer 资源以防止数据丢失。

来源

TextWriter 类文档

本教程介绍了使用 TextWriter 在 C# 中写入文本数据,包括基本操作、编码、格式化和资源管理。

作者

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

列出所有 C# 教程