Tcl/Tk Insight: 字符编码

@ 2011-05-25 18:58:48
标签:

讨论字符编码实际上可以涉及两种不同目的的编码:

  • 字符编码:数值和字符的映射关系。毕竟计算机实际上只有数值的概念。
  • 传输编码:传输数据所使用的编码。

虽然两者有时看起来是同一样东西,但实际概念上是有区别的。

字符编码

“字符编码”是为了建立字符和数值之间的映射关系,可以认为就是建立一张表格。

比如英文字符,一个字节的数值就可以表示出来。形成一种编码标准,就是很基础的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中的编码

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内置提供了用于编码转换的命令

encoding 命令

Tcl中的字符编码转换都是相对于Tcl的内部编码,即Unicode(BE)来说的。

encoding convertfrom

encoding convertfrom 用于把字符串按照指定编码转换为Tcl的内部编码。

比如: encoding convertfrom gb2312 $text

这时,$text 是作为一个字节流来解析的。而在Tcl中,实际上$text中的每个字符是占用两个字节。在进行转换时实际上高位字节就被忽略了,而只取用低位字节。

encoding convertto

encoding convertto 用于把字符串从Tcl内部编码转换为指定的编码。

比如: encoding convertto gb2312 $text

转换后的字符串也是一个字节流,每个字节实际上需要两个字节来表示——因为Tcl内部采用Unicode编码。

encoding system

encoding system 返回系统所用的编码。encoding system utf-8则用来设置系统编码为“utf-8”。

所谓系统编码,实际上是Tcl在和操作系统交互时所用的编码。

一种常见的情况是用source命令包含一个文件的时候,Tcl会使用系统编码来读取文件。这时,如果被包含的文件使用的编码方式和Tcl的系统编码不一致的情况,就可能会产生乱码。这种情况可以给source命令加上-encoding选项。比如:

source -encoding gb2312 my.tcl

一种建议是,Tcl程序和Tcl脚本文件都使用 utf-8 编码。

一些常见字符编码

  • Unicode-BE : Tcl内部使用的字符编码,实际上是 UTF-16BE
  • unicode : encoding names中列出的unicode实际上指的是 UTF-16LE
  • utf-8 : 网络上常用的编码格式
  • cp1252 : 默认的系统编码(encoding system),West European
  • cp936 : 可以认为是中文GBK编码
  • gb1988 : 中文字条编码

文件IO中的编码

先看一段程序:

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 命令。

标签:

分享到:
comments powered by Disqus

47/50ms