讨论字符编码实际上可以涉及两种不同目的的编码:
虽然两者有时看起来是同一样东西,但实际概念上是有区别的。
“字符编码”是为了建立字符和数值之间的映射关系,可以认为就是建立一张表格。
比如英文字符,一个字节的数值就可以表示出来。形成一种编码标准,就是很基础的ASCII码。
比如中文字符,需要两个字节才能表示完整。中国有自己的映射表,作为一种标准,比如GB2312,GBK等。
国际上的多字节字符标准主要是Unicode,目的是包含世界上的绝大部分字符。
给字符编码之后很显然是需要存储和传输的。计算机的世界里的存储和传输自然是指二进制数据,主要是指字节。
比如Unicode编码中,汉字“非”对应的数值是十六进制的0x975E
,传输和保存时就有几种不同的选择:
97 5e
:这是相应数值的Big Endian表示。5e 97
:这是相应数值的Little Endian表示。e9 9d 9e
:只是相应数值的UTF-8编码表示。而汉字“非”实际上也可以用GB2312来编码。
+--------------------------+ auto +------+ encoding convertfrom | Tcl String (Unicode BE) | <------> | OS | +--------------------------+ +------+ unicode utf-8 gb2312 ... +--------------------------+ binary format encoding converto | Bytes | +--------------------------+ binary scan +--------+--------+--------+ fconfigure -encoding | Output | File | Socket | +--------+---+----+--------+ | +------------+-------------+ encoding system | Default Encoding | +--------------------------+
Tcl命令encoding
的文档里提到:
Strings in Tcl are encoded using 16-bit Unicode characters.
Different operating system interfaces or applications may generate strings in other encodings such as Shift-JIS. The encoding command helps to bridge the gap between Unicode and these other formats.
实际上是指Tcl内部使用Unicode字符编码(而不是GB2312)。进一步说是Unicode BE(Big Endian)来表示字符。
比如字符串 非是非 - noyesno
内部表示为
975e 662f 975e 0020 002d 0020 006e 006f 0079 0065 0073 006e 006f
每两个字节表示一个字符。或者说,在Tcl中,每个字符其实都占用两个字节,即使是ASCII码字符也是占用两个字节的(???)。
Tcl内置提供了用于编码转换的命令
Tcl中的字符编码转换都是相对于Tcl的内部编码,即Unicode(BE)来说的。
encoding convertfrom
用于把字符串按照指定编码转换为Tcl的内部编码。
比如: encoding convertfrom gb2312 $text
这时,$text 是作为一个字节流来解析的。而在Tcl中,实际上$text中的每个字符是占用两个字节。在进行转换时实际上高位字节就被忽略了,而只取用低位字节。
encoding convertto
用于把字符串从Tcl内部编码转换为指定的编码。
比如: encoding convertto gb2312 $text
转换后的字符串也是一个字节流,每个字节实际上需要两个字节来表示——因为Tcl内部采用Unicode编码。
encoding system
返回系统所用的编码。encoding system utf-8
则用来设置系统编码为“utf-8”。
所谓系统编码,实际上是Tcl在和操作系统交互时所用的编码。
一种常见的情况是用source
命令包含一个文件的时候,Tcl会使用系统编码来读取文件。这时,如果被包含的文件使用的编码方式和Tcl的系统编码不一致的情况,就可能会产生乱码。这种情况可以给source
命令加上-encoding
选项。比如:
source -encoding gb2312 my.tcl
一种建议是,Tcl程序和Tcl脚本文件都使用 utf-8 编码。
encoding names
中列出的unicode实际上指的是 UTF-16LE先看一段程序:
set fp [open "t.txt" "r"] set line [get $fp] puts $line close $fp
如果文件"t.txt“包含中文字符,并且使用的是GB2312编码,而Tcl的encoding system
返回的则是utf-8,上面puts语句行则可能显示乱码。 因为文件指针 $fp 默认使用了系统编码 utf-8。解决办法是在打开文件后显式指定文件所用的编码,命令如下:
fconfigure $fp -encoding gb2312
其作用相当于对文件中的原始字节流应用 encoding convertfrom gb2312
命令。