0x00 答案
下面是C#判断给定的byte[]是否是指定UTF-8编码的代码。代码后有关于字符编码的一些知识,有兴趣可以移步。
private static byte UTF8CharacterMask1Byte = 0b1000_0000;
private static byte Valid1Byte = 0b0000_0000;//0b0xxx_xxxx
private static byte UTF8CharacterMask2Byte = 0b1110_0000;
private static byte Valid2Byte = 0b1100_0000;//0b110x_xxxx
private static byte UTF8CharacterMask3Byte = 0b1111_0000;
private static byte Valid3Byte = 0b1110_0000;//0b1110_xxxx
private static byte UTF8CharacterMask4Byte = 0b1111_1000;
private static byte Valid4Byte = 0b1111_0000;//0b1111_0xxx
private static byte UTF8CharacterMaskForExtraByte = 0b1100_0000;
private static byte ValidExtraByte = 0b1000_0000;//0b10xx_xxxx
public static bool IsValidUTF8ByteArray(byte[] bytes)
{
short extraByteCount = 0;
foreach (byte bt in bytes)
{
if (extraByteCount > 0)
{
extraByteCount--;
// Extra Byte Pattern.
if ((bt & UTF8CharacterMaskForExtraByte) != ValidExtraByte)
return false;
continue;
}
else
{
// 1 Byte Pattern.
if ((bt & UTF8CharacterMask1Byte) == Valid1Byte)
{
continue;
}
// 2 Bytes Pattern.
if ((bt & UTF8CharacterMask2Byte) == Valid2Byte)
{
extraByteCount = 1;
continue;
}
// 3 Bytes Pattern.
if ((bt & UTF8CharacterMask3Byte) == Valid3Byte)
{
extraByteCount = 2;
continue;
}
// 4 Bytes Pattern.
if ((bt & UTF8CharacterMask4Byte) == Valid4Byte)
{
extraByteCount = 3;
continue;
}
// invalid UTF8-Bytes.
return false;
}
}
return extraByteCount == 0;
}
0x01 UTF-8编码和Unicode字符集
讨论字符编码的时候总是容易混淆Unicode,UTF-8,UTF-16,UTF-32等概念。
所以在正式开始之前,先确定一下。
什么是字符集,什么是编码(Encoding)。两个关键概念。
1. 字符集(Charset or Character set):
按字面理解,就是字符的集合。不过没有想象那么简单,在RFC2278里"Charset"有如下定义
2.3 Charset
The term “charset” (see historical note below) is used here to refer
to a method of converting a sequence of octets into a sequence of
characters. This conversion may also optionally produce additional
control information such as directionality indicators.
如果用C#的Method来举例子说的话,Charset就像下面这个方法用一样。用于转换byte[]到char[]
public char[] Charset(byte[]);
这个定义和我们想象中的一个字符一个编号的字符集差得很远,不过这个是标准里的定义,也有过争论。而RFC2278里提到的另一个术语Coded Character Set(CCS)就和我们所常认为的字符集的意思能吻合。
2.4. Coded Character Set
A Coded Character Set (CCS) is a mapping from a set of abstract
characters to a set of integers. Examples of coded character sets are
ISO 10646 [ISO-10646], US-ASCII [US-ASCII], and the ISO-8859 series
[ISO-8859].
例如US-ASCII定义了128个字符到0-127数字的映射。
映射字符到数字,bingo了!我们常说的ASCII、Unicode等其实都是Coded Character Set。而非Charset,不过非学术上已经把charset和coded character set已经混为一谈了,以下为了讨论方便还是用字符集来指代Coded Character Set。(这俩关键翻译成中文都是字符集…笔者真心搞不懂Charset和Character Set的差别为毛这么大。如果有知道的朋友剋呀留言告诉笔者。)
2. 编码(Encoding):
作为常被我们念叨的编码,其实是一个信息转换的过程,视网膜会对进入的光线进行编码然后通过神经送入大脑,大脑然后对其解码。这便是我们看到世界的过程。
而计算机(电脑)也差不多,从别人手机上发送的字符串“123?“编码成二进制码后经过网络传输,发送到另一个设备上,这个设备的程序把二进制码解码后拿到同样的“123?“然后画在屏幕上。
而从"123?" → 二进制码的过程叫做编码。对于字符编码,它被叫做Character Encoding (link to wiki)。
对于一个字符‘?‘要编码到二进制码,分为三个步骤,Unicode 标准定义[4]。
- Character → Code Point (Through Coded Character Set)
- Code Point -> Code Units(Through Character Encoding Form)
- Code Units -> Sequence of bytes (Through Character Encoding Scheme)
我们用字符集Unicode,UTF-8编码来看一下笑哭这个表情如何被编码为UTF-8二进制码的。
1. Unicode字符集: Character -> Code Point
'?' → 0x1F602 (Decimal: 128514)
2. UTF -8 CEF(Character Encoding Form): Code Point -> Code Units
0x1F602 → 0xF0 0x9F 0x98 0x82 // 4个Code Units,UTF-8每个Code Unit为8-bits
3. UTF-8 CES(Character Encoding Scheme): Code Units -> Bytes
0xF0 0x9F 0x98 0x82 → 0xF0 0x9F 0x98 0x82
// 因为UTF-8的Code Unit本身就是8位,所以这一步实际上在UTF-8是省略的,但是在UTF-16里,有BE(Big Endian)和LE(Little Endian)的区别。
// 所以第三步的时候会把无序的Code Units转换到指定顺序的Bytes。
0x02 总结
- 我们常说的字符集,一般指的的Coded Character Set 而非 Charset。
- Unicode是一种Coded Character Set。
- 想注册Character Set可以参考RFC 2278 - IANA Charset Registration Procedures
- 已有的Character Sets可以参考Character Sets - IANA
- 对于一个字符转换到二进制码,在Unicode标准里,被分成了三步。(字符→Code Point(数字)→Code Units->Byte Sequence)
- 5的每一步都有设计各自的专有名词(CCS、CEF和CES)
- 对于UTF-8编码来说,没有第三步,因为UTF-8是面向字节编码的。
- 对于UTF-16、UTF-32编码来说,第三步的关键操作是指定BOM(Byte Order Mask),来告诉接收方编码好的UTF-16、UTF-32的Bytes是Big Endion(BE)的还是Little Endian(LE)的。
以上,希望你能有所收获。
参考文献:
[1] “RFC 3629 - UTF-8, a transformation format of ISO 10646”, Internet Engineering Task Force.(2019/03/23) Retrieved https://tools.ietf.org/html/rfc3629
[2] “Character Sets”, IANA.(2019/03/23) Retrieved http://www.iana.org/assignments/character-sets/character-sets.xhtml
[3] “RFC 2278 - IANA Charset Registration Procedures”, Internet Engineering Task Force.(2019/03/23) Retrieved https://www.ietf.org/rfc/rfc2278.txt
[4] “Unicode Technical Report #17 - UNICODE CHARACTER ENCODING MODEL”, http://unicode.org/reports/tr17/