解决了Erlang的UTF-8转Unicode中文显示问题

来源:互联网 发布:淘宝香港订单怎么发货 编辑:程序博客网 时间:2024/06/05 01:17
最近想用Erlang新版本R14B下的wxErlang做点事情,却又遭遇Unicode困扰。没想到,很快就彻底弄清了问题的原因,找到了解决之道。

Erlang能够处理Unicode,是从版本R13A开始的。不过,经过中间7、8个版本,直到目前的R14B03,Erlang只能在werl.exe开启的shell中,把UTF-8多字节编码,解析成双字节构成的单一Unicode字符。而至少在R13A和R13B中,由erlc.exe编译非ASCII字符时,生成的UTF-8存在很大问题,甚至让人怀疑是假的。

发现这个问题,是对R13B测试的结果。据此,我写了《Erlang 中文简体字 GB2312 转 Unicode 的办法》。这两天,又发现了更为确凿的证据。

打开R13B的wxErlang例程sudoku_gui.erl,把函数create_window() 的第一行
    Frame = wxFrame:new(wx:null(), -1, "Sudoku", []),
替换成以下内容:
    Bin2 = unicode:characters_to_binary("中文窗口", utf8),
    L2 = unicode:characters_to_list(Bin2),
    gb2u:start(),
    U = gb2u:get_unicode(L2,[]),
    Frame = wxFrame:new(wx:null(), -1, U, []),
这里的L2本应是UTF-8,实际上却是GB2312。模块gb2u.erl,正是根据GB2312检索对应Unicode的。

目前的R14B已经彻底纠正了这个问题,编译模块时,即使不显式调用unicode库函数,非ASCII码的数据,也会自动编译成UTF-8。于是,我写的那个gb2u.erl,只能用于R13A和R13B这两个版本,在其他版本没有用武之地,它不能根据UTF-8找出对应的Unicode。

怎么办呢?反复查看琢磨Erlang和Python中UTF-8数据的格式,终于看出了规律,GB2312的汉字转成UTF-8时,绝大多数是3个字节。把gb2u.erl稍做改动,有了下面的程序。

def all_gb2312():  
    s = '''-module(utf82u). 
-export([start/0,utf82unicode/2]). 
utf82unicode([],U) ->
    lists:reverse(U); 
utf82unicode([A|T],U) when A < 128 -> 
   utf82unicode(T,[A|U]); 
utf82unicode([A,B,C|Z],U) ->  
   H = get({A,B,C}), 
   utf82unicode(Z,[H|U]). 
start() ->\n'''  
    f = open('utf82u.erl','w')  
    f.write(s)  
    for row in range(161,248):  
        if row > 169 and row < 176:  
            continue  
        for col in range(161,255):  
            ch1 = chr(row)  
            ch2 = chr(col)  
            w = ch1+ch2  
            try:  
                utf8 = w.encode('utf8')  
                uc = utf8.decode('utf8')
            except:  
                continue
            s='put({%s,%s,%s},%s),\n'%(
                ord(utf8[0]),ord(utf8[1]),ord(utf8[2]),ord(uc))  
            f.write(s)  
    f.close()  

UTF-8与Unicode的转换,有具体的算法。网上也可以找到。我也找了个看了看,感觉要弄明白不算太难,但也得花时间认真琢磨,这事儿让人烦:既然有简单的办法,何必费那个劲呢。


上面Python程序生成的Erlang程序,在R14B上试了试。

打开R14B的wxErlang例程sudoku_gui.erl,把函数create_window() 的第一行
    Frame = wxFrame:new(wx:null(), -1, "Sudoku", []),
替换成以下内容:

create_window() ->
    Bin2 = Unicode:characters_to_binary("中文窗口", uft8),
    L2 = Unicode:characters_to_list(Bin2),
    utf82u:start(),
    U = utf82u:utf82unicode(L2,[]),
    Frame = wxFrame:new(wx:null(), -1, U, []),

嗯,程序运行后,正确显示了“中文窗口”,UTF-8确实变成了Unicode。

不过,Erlang自有的GUI工具gs,可以生成UTF-8,但不能显示Unicode,不能使用上面的办法。

总之,Erlang在werl的shell中,可以生成Unicode。在此之外,它只能生成UTF-8。Erlang主要用于开发服务器,尤其是http、web服务器,在网页中UTF-8用量较大。但是,在locale本地程序平台上,Unicode的用途重要,目前Erlang把UTF-8转Unicode这个活计留给了用户。

另外,总结一下在学习Erlang,琢磨Unicode过程中的2个意外收获。

1、发现Erlang字典函数的瑕疵

Erlang的一大特点,是程序内存的“私有化”严格安全管理。没有全局变量、全局内存缓冲。

但我发现,在Erlang R13B之前,字典函数get/1和put/2管理的内存,超出本程序限制,在Erlang shell级全局可用。

虽然当时我并没想到这是一个bug,也没向Erlang官方反映。但在博客文章《Erlang 中文简体字 GB2312 转 Unicode 的办法(3)》发表后,Erlang方面纠正了这个问题。

2、发现Erlang R13B的UTF-8的瑕疵

本文前面已有详细介绍,不再重复。其后的版本,已做纠正。


注:用Python生成的Erlang模块utf82u.erl,已经上传到“我的资源”。


原创粉丝点击