ZetCode

C# 编码 - 编码和解码文本

最后修改于 2025 年 5 月 11 日

本文提供了关于在 C# 中编码和解码数据的全面指南。 了解文本是如何处理和存储的对于处理不同的字符集以及确保跨应用程序的无缝数据交换至关重要。

Unicode 是一种通用的计算机行业标准,旨在为世界上大多数书写系统中的文本提供一致的编码、表示和处理。 它确保来自不同语言和符号的字符在各种平台上得到正确表示,从而使全球化应用程序更加可靠。

在 C# 中,字符串是 Unicode 字符的集合。 它是一种存储字符序列的数据类型,通常以字节为单位,其中每个元素根据特定的字符编码表示一个字符。 在内部,C# 使用 UTF-16 编码,确保字符以标准化的方式存储,同时支持各种符号和语言。

编码是将一组 Unicode 字符转换为字节序列的过程,从而可以有效地存储或传输数据。 解码会逆转此过程,将编码的字节转换回可读的 Unicode 字符。 这些过程在处理文本输入、文件操作、网络通信和数据序列化中起着至关重要的作用。

.NET 提供了几种标准字符编码来适应不同的用例。 这些包括 ASCII、UTF-7 (现在已弃用)、UTF-8、UTF-16 和 UTF-32。 其中,UTF-8 因其有效处理各种字符,同时最大限度地减少存储需求而被广泛使用。 UTF-16 仍然是 C# 环境中的默认编码。

.NET 中的 System.Text.Encoding 类为编码和解码文本提供了强大的功能。 它使开发人员能够在不同的字符集之间进行转换,从而确保与不同系统交互时的数据完整性。 .NET 内部使用的默认编码是 UTF-16,可以通过 Encoding.Unicode 访问。

C# 编码 GetByteCount

GetByteCount 方法返回编码指定字符产生的字节数。

Program.cs
using System.Text;

string text = "one 🐘 and three 🐋";

int n = Encoding.UTF8.GetByteCount(text);
Console.WriteLine($"UTF-8: {n}");

n = Encoding.UTF32.GetByteCount(text);
Console.WriteLine($"UTF-32: {n}");

n = Encoding.Unicode.GetByteCount(text);
Console.WriteLine($"UTF-16: {n}");

n = Encoding.BigEndianUnicode.GetByteCount(text);
Console.WriteLine($"UTF-16BE: {n}");

n = Encoding.Latin1.GetByteCount(text);
Console.WriteLine($"Latin1: {n}");

n = Encoding.ASCII.GetByteCount(text);
Console.WriteLine($"ASCII: {n}");

该示例打印了使用指定的每种编码对给定字符串进行编码时产生的字节数。 此输出可帮助你了解不同的编码如何在内存中表示相同的字符串。 某些编码(如 UTF-8)对字符使用可变长度字节,而其他编码(如 UTF-32)使用固定长度。 字节数的差异显示了每种编码对于相同文本所需的空间量,这在优化存储或兼容性时非常重要。

$ dotnet run
UTF-8: 23
UTF-32: 68
UTF-16: 38
UTF-16BE: 38
Latin1: 19
ASCII: 19

C# 编码 GetBytes

GetBytes 方法返回一个字节数组,其中包含编码指定字符集的结果。

Program.cs
using System.Text;

string text = "one 🐘 and three 🐋";

Console.WriteLine("UTF-8 bytes");
byte[] uft8Data = Encoding.UTF8.GetBytes(text);
showBytes(uft8Data);

Console.WriteLine("UTF-16 bytes");
byte[] uft16Data = Encoding.Unicode.GetBytes(text);
showBytes(uft16Data);

Console.WriteLine("UTF-16BE bytes");
byte[] uft16BEData = Encoding.BigEndianUnicode.GetBytes(text);
showBytes(uft16BEData);

Console.WriteLine("Latin1 bytes");
byte[] latin1Data = Encoding.Latin1.GetBytes(text);
showBytes(latin1Data);

void showBytes(byte[] data)
{
    int i = 0;

    foreach (var e in data)
    {
        Console.Write($"{e.ToString("X4")} ");
        i++;

        if (i % 10 == 0)
        {
            Console.WriteLine();
        }
    }

    Console.WriteLine();
}

该示例使用 UTF-8、UTF-16、UTF-16BE 和 Latin1 编码将给定字符串编码为字节。 通过检查字节数组,你可以看到每种编码如何处理 Unicode 字符,尤其是基本 ASCII 范围之外的字符。 例如,UTF-8 和 UTF-16 可以表示表情符号和特殊符号,而 Latin1 会将不支持的字符替换为占位符。 这说明了为什么选择正确的编码对于保持数据完整性至关重要。

$ dotnet run
UTF-8 bytes
006F 006E 0065 0020 00F0 009F 0090 0098 0020 0061
006E 0064 0020 0074 0068 0072 0065 0065 0020 00F0
009F 0090 008B
UTF-16 bytes
006F 0000 006E 0000 0065 0000 0020 0000 003D 00D8
0018 00DC 0020 0000 0061 0000 006E 0000 0064 0000
0020 0000 0074 0000 0068 0000 0072 0000 0065 0000
0065 0000 0020 0000 003D 00D8 000B 00DC
UTF-16BE bytes
0000 006F 0000 006E 0000 0065 0000 0020 00D8 003D
00DC 0018 0000 0020 0000 0061 0000 006E 0000 0064
0000 0020 0000 0074 0000 0068 0000 0072 0000 0065
0000 0065 0000 0020 00D8 003D 00DC 000B
Latin1 bytes
006F 006E 0065 0020 003F 003F 0020 0061 006E 0064
0020 0074 0068 0072 0065 0065 0020 003F 003F

C# 编码 GetString

GetString 方法通过解码给定的字节序列来构建字符串。

Program.cs
using System.Text;

string text = "one 🐘 and three 🐋";

Console.WriteLine("UTF-8 bytes");
byte[] uft8Data = Encoding.UTF8.GetBytes(text);
string output = Encoding.UTF8.GetString(uft8Data);
Console.WriteLine(output);

Console.WriteLine("UTF-16 bytes");
byte[] uft16Data = Encoding.Unicode.GetBytes(text);
output = Encoding.Unicode.GetString(uft16Data);
Console.WriteLine(output);

Console.WriteLine("UTF-16BE bytes");
byte[] uft16BEData = Encoding.BigEndianUnicode.GetBytes(text);
output = Encoding.BigEndianUnicode.GetString(uft16BEData);
Console.WriteLine(output);

Console.WriteLine("Latin1 bytes");
byte[] latin1Data = Encoding.Latin1.GetBytes(text);
output = Encoding.Latin1.GetString(latin1Data);
Console.WriteLine(output);

在此示例中,我们首先使用 GetBytes 将字符串编码为字节数组。 然后,我们使用 GetString 将字节解码回字符串。 展示了四种不同的编码。

此过程显示了编码和解码如何协同工作。 如果对两个操作使用相同的编码,则原始字符串将被保留。 但是,如果编码无法表示某些字符(如 Latin1 和表情符号),则这些字符将丢失或被替换。 这突出了在传输或存储文本数据时匹配编码的重要性。

$ dotnet run
UTF-8 bytes
one 🐘 and three 🐋
UTF-16 bytes
one 🐘 and three 🐋
UTF-16BE bytes
one 🐘 and three 🐋
Latin1 bytes
one ?? and three ??

Latin1 编码无法表示表情符号,因此它们显示为问号。

C# Base64 编码和解码

Base64 编码通常用于将二进制数据编码为文本,使其可以安全地通过仅支持文本的协议进行传输。 在 C# 中,你可以使用 Convert.ToBase64StringConvert.FromBase64String 方法来编码和解码数据。

Program.cs
using System.Text;

string text = "Encode this string to Base64!";

// Encode to Base64
byte[] bytes = Encoding.UTF8.GetBytes(text);
string base64 = Convert.ToBase64String(bytes);
Console.WriteLine($"Base64: {base64}");

// Decode from Base64
byte[] decodedBytes = Convert.FromBase64String(base64);
string decodedText = Encoding.UTF8.GetString(decodedBytes);
Console.WriteLine($"Decoded: {decodedText}");

此示例展示了如何将字符串编码为 Base64,然后将其解码回原始字符串。 Base64 可用于编码需要存储或作为纯文本传输的数据,例如在 XML 或 JSON 文件中,或者在基于文本的格式中嵌入二进制数据时。

C# Encoding.Convert

Encoding.Convert 方法将字节数组从一种编码转换为另一种编码。

Program.cs
using System.Text;

string text = "one 🐘 and three 🐋";

byte[] utf16Data = Encoding.Unicode.GetBytes(text);
byte[] utf8Data = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, utf16Data);

Console.WriteLine("UTF-16 bytes");
showBytes(utf16Data);

Console.WriteLine();

Console.WriteLine("UTF-8 bytes");
showBytes(utf8Data);

Console.WriteLine();
string output = Encoding.UTF8.GetString(utf8Data);
Console.WriteLine(output);


void showBytes(byte[] data)
{
    int i = 0;

    foreach (var e in data)
    {
        Console.Write($"{e.ToString("X4")} ");
        i++;

        if (i % 10 == 0)
        {
            Console.WriteLine();
        }
    }

    Console.WriteLine();
}

在此示例中,我们将 UTF-16 字节转换为 UTF-8 字节。 当你需要更改数据的编码时,例如从一个编码的文件中读取数据并将其保存为另一种编码时,Encoding.Convert 方法非常有用。 这确保了期望不同编码的系统之间的兼容性,并有助于防止转换期间的数据损坏或丢失。

$ dotnet run
UTF-16 bytes
006F 0000 006E 0000 0065 0000 0020 0000 003D 00D8 
0018 00DC 0020 0000 0061 0000 006E 0000 0064 0000 
0020 0000 0074 0000 0068 0000 0072 0000 0065 0000 
0065 0000 0020 0000 003D 00D8 000B 00DC 

UTF-8 bytes
006F 006E 0065 0020 00F0 009F 0090 0098 0020 0061 
006E 0064 0020 0074 0068 0072 0065 0065 0020 00F0 
009F 0090 008B 

one 🐘 and three 🐋

C# 使用编码读取/写入数据

接下来,我们将数据写入文件并使用指定的编码将其读回。

Program.cs
using System.Text;

string text = "one 🐘 and three 🐋";

using var fs = new FileStream("data.txt", FileMode.OpenOrCreate);
using var sw = new StreamWriter(fs, Encoding.UTF8);
sw.Write(text);

在此示例中,我们使用 Encoding.UTF8 将文本写入文件。

在写入文件时指定编码可确保文本被正确存储,并且可以被期望相同编码的其他应用程序读取。 UTF-8 是一种常见的选择,因为它具有兼容性和效率,尤其是在处理国际文本或跨平台共享文件时。

using var sw = new StreamWriter(fs, Encoding.UTF8);

StreamWriter 的第二个参数指定要使用的字符编码。

$ dotnet run
$ file data.txt 
data.txt: Unicode text, UTF-8 (with BOM) text, with no line terminators
$ cat data.txt 
one 🐘 and three 🐋

接下来,我们从文件中读取数据。

Program.cs
using System.Text;

using var fs = new FileStream("data.txt", FileMode.Open);
using var sr = new StreamReader(fs, Encoding.UTF8);

string? text = sr.ReadLine();
Console.WriteLine(text);

我们使用 StreamReader 读取数据,并将编码指定为第二个参数。 通过向 StreamReader 提供正确的编码,可以确保文件中的字节被正确地解释为字符。 这对于读取使用不同编码创建的文件至关重要,并有助于避免误解或损坏文本的问题。

$ dotnet run
one 🐘 and three 🐋

来源

Encoding 类 - 语言参考

在本文中,我们探讨了在 C# 中编码和解码数据。

作者

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

列出所有 C# 教程