erlang 中文支持
转载:
http://blog.codingnow.com/2008/12/erlang_shell_utf-8.html
发现在Ubuntu下用apt装的Erlang(5.6.5)的Shell不支持中文,这让我郁闷了一把。:(
好在Erlang是开源的,有源码在手,有何畏惧。晚上,我卯足了劲研究Erlang的sourcecode,想找到不支持中文显示的原因。
表现是这样的,我的环境是UTF-8,当我输入汉字时,在ErlangShell里立刻被转义为了\xxx这样的8进制数。在《Erlang程序设计》的2.11/21页写道,“这实际上是显示终端的字符集和区域设定有问题”。我改了半天,都没有把汉字鼓捣出来,感觉不是我的设置问题。
但是,在同事的Windows机器上的ErlangShell下却是可以正确显示中文的。
我想,读读源码也好,正好可以实际体会一下Erlang的代码风格。
--------------------------------------------------------------------------------
在读代码的过程中绕了不少弯路,找到许多貌似会影响输出的地方,改过后都没有效果。
比如在
erts/emulator/beam/erl_printf_term.c
里有一个
IS_CNTRL
的宏,为LATIN1的字符集hardcode了一些东西,有兴趣的同学可以看看,其中也有一些字符转换的工作。
类似的地方就不多说了,稍微浏览过Erlang底层的一些C代码后,感觉质量还不错,不过在可移植性和C语言标准上,没有Lua的源代码严谨。因为Lua里就没有为某些字符集特别定制的代码。好吧,我就看了几个小时而已,权当是一个偏见。
最终找到了关键的地方,一共有两处:
一处在
lib/stdlib/src/io_lib.erl
文件里,打开这个文件就可以看到前面有些关于ISO8859-1/Latin-1的信息。搜索这个文件,会发现它把$\240到$\377之间的字符认为是可以打印的。但是对于UTF-8编码来说,这不够。把$\240改成$\200即可。
在ErlangShell里,只有整个整数数组里的数字都是可打印字符,才会显示成"xxxxx"的形式,否则就是[xxx,xxx,xxx,xxx]这样的。做了这个修改后,就不会在有UTF-8的中文串被转换为[数字]了。
第二处在C代码里。
因为Erlang的内部是自己管理的若干进程(非操作系统进程),为了让用户输入和输出可以统一,所以实际上,和用户交互的IO是序列化在一个进程里完成的。因此,我们看到的io:format的输出,其实是向管理IO的进程发了一个消息而已。
为了找到这个最终真正处理输入输出的进程,我查看了Erlang的源代码,就是lib/kernel/src的部分。最后发现,是user模块做最终的汇总,然后交给
user_drv
去处理IO。
但是,我们知道Erlang自己是不可能完成IO操作的,必然会涉及C实现的代码,去跟OS交互。在
user_drv
里我们发现了一个叫
tty_sl
的东西。这就是C实现的终端部分了。
Erlang和其它语言交互的方式很有趣,是用进程间(不一定是操作系统级的)通讯完成的,用二进制数据流交互。这跟我们游戏服务器的过进程结构很像。不得不说,Erlang做的非常优美。今天太晚,不展开评论了。
回到主话题上,这个
tty_sl
的实现在
/erts/emulator/drivers/unix/ttsl_drv.c
。读一读这个源文件,大概就能理解C模块如何跟Erlang交互的了。
关于中文显示的问题,正在于,这个实现中,hardcode写死了当字符大于等于128时的处理方法:转换为\八进制表示。
我简单增加了一个ISPRINT宏,替换掉原来的isprint调用,让大于等于128的字符也返回true。重新编译安装后,Erlang的Shell就可以在Ubuntu下正确显示中文了。
不过还有一个问题,光标的处理不太正确,比如退格键可以消掉半个汉字。我想,比较简单并健壮的修改方法应该是把这个tty服务的内码改成UCS-2的,这样比较容易让单个汉字变成原子的。当然,想办法让它正确处理UTF-8的内码也行。
留到以后再改吧。