在实际编程中经常会碰到需要读取一个文本文件的内容并将其显示到程序中的情况。如果文件中所有的内容都以ASCII方式编码固然简单(通常包含英文字母和数字的文件,比如readme之类),但遇到包含其他语言字符,如中文和日文之类就必须在显示之前知道其编码方式。这是因为很多程序在显示文本内容时只接受UNICODE,故我们必须对非UNICODE的编码方式进行转换。由于谈到UNICODE时通常讨论的都是UCS-2,文中将只对UCS-2文件进行分析。
本文提供了针对UCS-2 Little Endian,UTF-8和GB2312三种方式的文本文件进行编码识别的方法:
1. UCS-2文件
UCS-2 Little Endia文件以FF FE开头,所以如果一个文件以该标志开头,可以判断出其为UCS-2 Little Endian编码。类似的,UCS-2 Big Endia文件以FE FF开头。当读取UCS-2编码的大文件时,如果因为内存限制而无法一次性将所有内容都读出来的话,就需要分批读取。由于UCS-2是长度固定的编码方式(两个字节表示一个UNICODE字符),因此只要保证每次都采用偶数长度的缓冲区进行读取,转换就不会出现乱码。
2. UTF-8文件
标准UTF-8编码的文件是以EF BB BF开头,如果读到该标志,则可以以UTF-8方式进行UCS-2的转码。但是当程序读取部分内容进行编码的时候则与UCS-2方式有很大的不同!根据规范,采用UTF-8方式进行编码的字符可以有从1到6个字节的不等长度。因此程序必须从缓冲区末尾开始判断最后一个UTF-8字符出现的位置,以防出现只包含字符的部分内容从而导致转码失败。
UTF-8的编码规则如下表所示:
因此逆向查找必须找到以下信息以确定UTF-8字符的开始字节:
3. GB2312文件
GB2312为简体中文汉字的编码方式,在网络上很常见。GB2312编码的字符可以有一个或两个字节。其中和ASCII兼容的部分都占用一个字节。每个汉字由两个汉字表示。GB2312的编码范围是0xA1A1-0x7E7E,去掉未定义的区域之后可以理解为实际编码范围是0xA1A1-0xF7FE。由于使用GB2312编码的文件不像UCS-2或UTF-8一样使用特殊的标志,因此很难判断其编码方式。当然也可以采取猜测的方式,例如对于非ASCII的双字节字符,其第一个字符处于0xA1到0x7E的范围之内。但这种方式并不保险,容易和其他编码方式冲突。
总之,对于不同编码方式的文件,除非有特殊的标志可以标示,否则只能采用猜测之类的办法,并且不能保证每次成功。估计只有等到未来UNICODE能够一统天下的时候才不会再有这些问题。:)
本文提供了针对UCS-2 Little Endian,UTF-8和GB2312三种方式的文本文件进行编码识别的方法:
1. UCS-2文件
UCS-2 Little Endia文件以FF FE开头,所以如果一个文件以该标志开头,可以判断出其为UCS-2 Little Endian编码。类似的,UCS-2 Big Endia文件以FE FF开头。当读取UCS-2编码的大文件时,如果因为内存限制而无法一次性将所有内容都读出来的话,就需要分批读取。由于UCS-2是长度固定的编码方式(两个字节表示一个UNICODE字符),因此只要保证每次都采用偶数长度的缓冲区进行读取,转换就不会出现乱码。
2. UTF-8文件
标准UTF-8编码的文件是以EF BB BF开头,如果读到该标志,则可以以UTF-8方式进行UCS-2的转码。但是当程序读取部分内容进行编码的时候则与UCS-2方式有很大的不同!根据规范,采用UTF-8方式进行编码的字符可以有从1到6个字节的不等长度。因此程序必须从缓冲区末尾开始判断最后一个UTF-8字符出现的位置,以防出现只包含字符的部分内容从而导致转码失败。
UTF-8的编码规则如下表所示:
UCS-2取值范围 | UTF-8编码格式 |
0x00000000 - 0x0000007F | 0xxxxxxx |
0x00000080 - 0x000007FF | 110xxxxx 10xxxxxx |
0x00000800 - 0x0000FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0x00010000 - 0x001FFFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
0x00200000 - 0x03FFFFFF | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
0x04000000 - 0x7FFFFFFF | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
因此逆向查找必须找到以下信息以确定UTF-8字符的开始字节:
- ASCII兼容的字符。这种字符的UTF-8编码值小于0x7F
- 非ASCII字符的UTF-8编码中的第一个字符总是在范围0xC0到0xFD。如果找到此范围内的字符,则可以确定一个UTF-8字符的开始。另外第一个编码的字节中1的个数指定了该字符总共的字节数。
3. GB2312文件
GB2312为简体中文汉字的编码方式,在网络上很常见。GB2312编码的字符可以有一个或两个字节。其中和ASCII兼容的部分都占用一个字节。每个汉字由两个汉字表示。GB2312的编码范围是0xA1A1-0x7E7E,去掉未定义的区域之后可以理解为实际编码范围是0xA1A1-0xF7FE。由于使用GB2312编码的文件不像UCS-2或UTF-8一样使用特殊的标志,因此很难判断其编码方式。当然也可以采取猜测的方式,例如对于非ASCII的双字节字符,其第一个字符处于0xA1到0x7E的范围之内。但这种方式并不保险,容易和其他编码方式冲突。
总之,对于不同编码方式的文件,除非有特殊的标志可以标示,否则只能采用猜测之类的办法,并且不能保证每次成功。估计只有等到未来UNICODE能够一统天下的时候才不会再有这些问题。:)