前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Erlang & Unicode

Erlang & Unicode

作者头像
EltonZheng
发布2021-01-26 11:02:03
1.6K0
发布2021-01-26 11:02:03
举报
文章被收录于专栏:Elton的技术分享博客

Erlang的string实际上就是整数项组成的list,注意string的编解码使用是使用ISO-latin-1字符集,即:每8字节当成一个整体进行解读;这个字符集是Unicode的子集.Erlang list编解码很容易扩展到整个unicode编码:由于编码是整数和字符的对应关系,只要list中的整函数是有效的Unicode codepoint就可以找到对应的字符;

二进制数据处理起来就麻烦一些了,二进制数据是紧凑排列的:一个字节代表一个字符,而不是两个字(word)一个字符,这里如果存在疑问可以查看erlang官方文档中关于内存消耗的列表:http://www.erlang.org/doc/efficiency_guide/advanced.html .平时我们使用的erlang:list_to_binary,常规的Erlang string(ISO-latin编码的string)可以逐字节逐字符顺利转成binary.但是超出ISO-latin编码范围就会出错了,看下面的例子:

代码语言:javascript
复制
1> L=[10,12,23,45].
[10,12,23,45]
2> list_to_binary(L).
<<10,12,23,45>>
3> L2= "中国".      
[20013,22269]
4> list_to_binary(L2).
** exception error: bad argument
     in function  list_to_binary/1
        called as list_to_binary([20013,22269])
5> unicode:characters_to_list(L2).
[20013,22269]
6> unicode:characters_to_binary(L2). %%注意"中国"用二进制占用了6个字节
<<228,184,173,229,155,189>>

UTF-8是Erlang二进制处理的标准编码形式,一旦出现需要处理Unicode二进制数据的场景,默认就会选择UTF8编码.比特语法支持使用其它的编解码方式,但是erlang类库中处理二进制都是使用UTF-8编码.字符串可以接受Unicode字符,但是Erlang的语言元素编写还是限制在ISO-latin-1的范围内.Erlang编译过程依然是使用ISO-latin-1编码,这样的影响是什么呢?代码中出现的Unicode字符会有部分无法在ISO-latin-1找到对应的字符,那怎么办呢?没关系,找不到对应的字符就按照整形数去处理就好了.

Erlang Shell对unicode的支持要强一些,但是也并不完善,下面我们通过一系列实验来看上面的问题,在test模块里面我们准备两条测试数据:

代码语言:javascript
复制
data()->
   "hello 中国 ren".
data2()->
   <<"hello 中国 ren">>.

启动Erlang Shell,我们对比一下数据之间的差异:

代码语言:javascript
复制
1> "hello 中国 ren".  %%在shell中输入包含中文的string,可以看到它就是一个List,注意中文字符对应的数值
[104,101,108,108,111,32,20013,22269,32,114,101,110]
2> test:data().        %%注意下面的数据,中文部分数值已经被切割成两组数据
[104,101,108,108,111,32,228,184,173,229,155,189,32,114,101,
110]   
3> <<"hello 中国 ren">>.  %%而这样的数据在shell中直接出错了 (注意:windows下可能是正常的)
** exception error: bad argument
4> test:data2().   %%看看这里二进制的输出,数值上是和v(2)的数值上是一致的
<<104,101,108,108,111,32,228,184,173,229,155,189,32,114,
  101,110>>
5> unicode:characters_to_binary(v(1)).  %%把v(1)的结果转成二进制,为什么不用list_to_binary?往下看
<<104,101,108,108,111,32,228,184,173,229,155,189,32,114,
  101,110>>
6> io:format("~ts~n",[v(1)]). %%注意这里格式化的时候使用的修饰符是~ts
hello 中国 ren
ok
7> io:format("~ts~n",[v(2)]).  %%v(2)输出的内容并不是我们期望的                       
hello 中国 ren
ok
8> io:getopts().  %%是不是觉得少检查了点什么?是的 看看环境编码
[{expand_fun,#Fun},
{echo,true},
{binary,false},
{encoding,unicode}]
9> list_to_binary(v(1)). %%看到了吧,这样会异常的
** exception error: bad argument
     in function  list_to_binary/1
        called as list_to_binary([104,101,108,108,111,32,20013,22269,32,114,101,110])
10> list_to_binary(v(2)).
<<104,101,108,108,111,32,228,184,173,229,155,189,32,114,
  101,110>>
14> 
</pre>

进行到这里,下面这个问题就有答案了,我们在Shell中执行下面的语句:
14>  re:run("hello 中国 ren", "[\x{4e00}-\x{9fff}]+", [unicode]).
{match,[{6,6}]}
15> 
然后我们把这条语句放在模块代码中执行:
re() ->
   re:run("hello 中国 ren", "[\x{4e00}-\x{9fff}]+", [unicode]).
执行结果:
15> test:re().
nomatch
16>

答案就是:在模块文件进行编译的时候使用的是ISO-latin-1,其中的中文并不在其字符集中,所以转成了两组数字!被转成两组数字之后,也就无法被正则表达式命中了.而在Erlang Shell中,中文字符可以被正确编码,所以会被正则命中.而仔细关注一下正则表达式,其实就是大致上覆盖了中文字符在unicode字符集中对应的数值区间.

   对于这种情况只要让unicode避开编译阶段就可以了,比如把这类文本放在外部文本中,下面立涛给的这份代码样例中演示了从外部文件读取文本内容,并匹配中文. https://gist.github.com/2768621 

一个细节"~ts" 修饰符
The Erlang compiler will interpret the code as ISO-8859-1 encoded text, which limits you to Latin characters."translation modifier" when working with Unicode texts. The modifier is "t". When applied to the "s" control character in a formatting string, it accepts all Unicode codepoints and expect binaries to be in UTF-8.
1> io:format("~ts",[unicode:characters_to_binary([20013])]).
中ok
2> io:format("~ts",[unicode:characters_to_binary([20013,22269])]).
中国ok
3>  L=[229,136,157,231,186,167], io:format("~ts", [list_to_binary(L)]).             
初级ok

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档