我会从这个问题开始:什么是字符?
- 逻辑标识:代码点。 Unicode 为每个字符分配一个数字,该数字不一定与任何位/字节形式相关。编码(如 UTF-8)定义到字节值的映射。
- 位和字节:编码形式。每个代码点一个或多个字节,值由使用的编码确定。
- 您在屏幕上看到的东西:字形。字形由一个或多个代码点创建。这是演示结束时的内容。
此代码将in.txt 从windows-1252 转换为UTF-8 并将其保存为out.txt。
using System;
using System.IO;
using System.Text;
public class Enc {
public static void Main(String[] args) {
Encoding win1252 = Encoding.GetEncoding(1252);
Encoding utf8 = Encoding.UTF8;
using(StreamReader reader = new StreamReader("in.txt", win1252)) {
using(StreamWriter writer = new StreamWriter("out.txt", false, utf8)) {
char[] buffer = new char[1024];
while(reader.Peek() > 0) {
int r = reader.Read(buffer, 0, buffer.Length);
writer.Write(buffer, 0, r);
}
}
}
}
}
这里发生了两个转变。首先,字节从windows-1252 解码到UTF-16(我认为是小端序)到char 缓冲区。然后将缓冲区转换为UTF-8。
代码点
一些示例代码点:
- U+0041 是 拉丁文大写字母 A (A)
- U+00A3 是 磅符号 (£)
- U+042F 是 西里尔大写字母 YA (Я)
- U+1D50A 是 MATHEMATICAL FRAKTUR CAPITAL G (?)
编码
在您使用字符的任何地方,它都会采用某种形式的编码。 C# 使用 UTF-16 作为其 char type,它定义为 16 位宽。
您可以将编码视为代码点和字节表示之间的表格映射。
CODEPOINT UTF-16BE UTF-8 WINDOWS-1252
U+0041 (A) 00 41 41 41
U+00A3 (£) 00 A3 C2 A3 A3
U+042F (Ya) 04 2F D0 AF -
U+1D50A D8 35 DD 0A F0 9D 94 8A -
System.Text.Encoding 类公开类型/方法来执行转换。
字形
您在屏幕上看到的字形可能由多个代码点构成。字符 e-acute (é) 可以用两个代码点表示,LATIN SMALL LETTER E U+0065 和 COMBINING ACUTE ACCENT U+0301。
('é' 通常由单个代码点 U+00E9 表示。您可以使用规范化在它们之间切换。但并非所有组合序列都具有单个字符等效项。)
结论
- 当您将 C# 字符串编码为编码时,您正在执行从 UTF-16 到该编码的转换。
- 编码可能是一种有损转换 - 大多数非 Unicode 编码只能编码现有字符的子集。
- 由于并非所有代码点都可以放入单个 C# 字符中,因此字符串中的字符数可能多于代码点数,并且代码点数可能大于呈现的字素数。
- 字符串的“长度”是上下文相关的,因此您需要知道所应用的含义并使用适当的算法。处理方式取决于您使用的编程语言。
- 在许多编码中为 Latin-1 字符赋予相同的值会使一些人产生 ASCII 错觉。
(这比我预期的要啰嗦一点,而且可能比你想要的要多,所以我会停下来。我写了一个更啰嗦的post on Java encoding here。)