最近在使用erlang做游戏服务器,而字符串在服务器编程中的地位是十分重要的,于是便想仔细研究下字符编码,以及erlang下的字符串处理。先从Unicode开始吧....

【Unicode】

Unicode标准确定世界绝大部分的字符编码值(code point),而对于code point的编码方式有很多种,这些编码方式称为UTF。我们需要区分开Unicode字符与Unicode编码,Unicode字符指的是Unicode标准中定义的编码值,这个值代表世界上独一无二的一个字符,而Unicode编码则是这些值在计算机中的表达方式,比如UTF-8就是Unicode的一种编码。这里我们主要谈论下Unicode编码在erlang下的处理,先谈谈以下几种编码吧...

(1)单字节编码

使用单个字节来保存字符编码,这种编码不能算是Unicode编码,因为它在Unicode标准出现前就有了,但他还是可以表示Unicode中编码小于256的符号。这部分字符集对应ISO-Latin-1字符集,在erlang中它就是latin1编码,这里的ISO-Latin-1和latin1不是同一个东东,一个是字符集,一个是编码.

(2)UTF-8

多字节编码,它使用1到4个字节来进行编码,也就是说UTF-8是变长的,使用的字节数会根据字符的编码大小而变化.它兼容按单字节编码的7bitASCII字符,因为这些字符在UTF-8中也只需要占据1个字节。当编码值大于127时UTF-8将使用多个字节来保存,并且在第一个字节里使用几个位来标注该字符是多字节的。因此UTF-8与大于127的单字节编码并不兼容,所以latin1与UTF-8并不是完全兼容的。

(3)UTF-16

它与UTF-8相似,也是多字节编码,只是它的基本单位是16位,也就是说所有Unicode字符至少占用2个字节,编码数大甚至会使用4个字节。一些系统和程序只允许UTF-16使用2个字节,因为它基本足够表达大多数字符了。当然由于基本单位大于了1个字节,UTF-16就存在大编,小编两个变种。

(4)UTF-32,UCS-4

最直接的编码方式,使用4个字节来保存字符。与UTF-16一样,存在大编,小编两个变种。

研究到这里,我产生了一个疑问,UTF-8和UTF16,32一样都是多字节编码,为什么UTF-8就不存在大编小编的变种呢?要解决这个问题,必须得明白UTF-8的具体编码方式才行,于是google...

如上图所示,UTF-8在表示128以下的字符时,使用一个字节,并且最高位是0,表示该字符是单字节。当需要多字节时,首位字节的高位由一个或多个1占据,并且1后面跟随一个0,来与code point的字节位分隔,后面的字节以10开始,10后面是code point的字节位。首位字节的1的个数表示了这个字符需要的字节数,这样处理字符的程序就不要去查看后面以10开始的字节数量,便知道了该字符需要的字节数是多少。

好了,了解了UTF-8的具体编码方式了,但还是没有解决之前的那个问题,就是UTF-8为什么没有大小编的变种?仔细研究了后,发现UTF-8还有一个约定,就是编码的高字节位在首字节,后面接着的字节依次保存后面的编码字节位。这样就约定了UTF-8固定的编码字节保存方式。而UTF-16,UTF-32并没有约定自己的编码保存方式,他们依赖与编码保存的环境,所以他们需要区别大小编。

UTF-16,32在文本流的第一个字符前,使用BOM(bytes order mark)来区别大小编。以UTF-16为例,它的BOM是FEFF,如果编码方编码的是FEFF,而解码方解码得到的是FFFE,那说明大小编不一致,就需要对解码的字节进行处理。当然解码方不处理BOM,那么FEFF会被当作ZWNBSP character处理,估计就是什么都不做的意思。BOM除了用来区分大小编码外,还可以区分编码类型,比如UTF-8的BOM是EF BB BF。

【erlang字符串与Unicode处理】

了解了Unicode,接着谈谈erlang下对字符串和Unicode的处理。在erlang下,没有单独的string类型,它使用list包含一组int来表示,一个int代表一个字符编码。在R13版本之前,它使用ISO-latin-1 (ISO8859-1)字符集来编码,在R13之后扩展到Unicode。list的表示方式很容易扩展到Unicode字符集,因为它使用一个int来表示字符。但如果要将list转换成二进制类型,Unicode就会有点问题。当code point < 256 时,字符是单字节编码,erlang可以使用latin1,那么code point在内存中的表示是一个挨着一个的,这样就可以直接使用erlang:iolist_to_binary/1将list转换到二进制数据。(在erlang中会使用这种方式来处理字符串,因为list是使用一个int来表示字符串,当只需要单字节编码时,用二进制类型保存更节约空间) 但是当code point >= 256时,转换成二进制时,就需要确定Unicode的编码方式,不能直接使用erlang:iolist_to_binary/1接口,需要使用unicode:characters_to_binary/{1,2,3}来转换。默认情况下,转换时erlang使用UTF-8作为标准的Unicode编码。

  • 在R16B版本之后,erlang允许使用UTF-8来对源代码编码,默认下使用latin1编码。可以通过在源代码文件前加入 %% -*- coding: utf-8 -*- 来设置编码方式。string和注释可以使用UTF-8,但函数名和atom还是使用ISO-latin-1字符集,这个有可能在R18改变。

  • 二进制类型的位语法中也加入了对Unicode的处理:

<<Ch/utf8,_/binary>> = Bin1,

<<Ch/utf16-little,_/binary>> = Bin2,

Bin3 = <<$H/utf32-little, $e/utf32-little, $l/utf32-little, $l/utf32-little, $o/utf32-little>>,

Bin4 = <<"Hello"/utf16>>

非常方便。。
  • erlang的输出函数会启发式的检测输入的list,binariy是否是可以打印的字符。默认检测的字符范围是ISO-Latin-1,也可以在启动时通过+pc指定为UTF-8。对于io(_lib):format/2函数,也可以使用~tp来被指定范围影响。

指定范围为Latin
$ erl +pc latin1 Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.10.1 (abort with ^G) 1> [1024]. [1024] 2> [1070,1085,1080,1082,1086,1076]. [1070,1085,1080,1082,1086,1076] 3> [229,228,246]. "åäö" 4> <<208,174,208,189,208,184,208,186,208,190,208,180>>. <<208,174,208,189,208,184,208,186,208,190,208,180>> 5> <<229/utf8,228/utf8,246/utf8>>. <<"åäö"/utf8>> 1> io:format("~tp~n",[{<<"åäö">>, <<"åäö"/utf8>>, <<208,174,208,189,208,184,208,186,208,190,208,180>>}]). {<<"åäö">>,<<"åäö"/utf8>>,<<208,174,208,189,208,184,208,186,208,190,208,180>>} 指定范围为UTF-8
$ erl +pc unicode Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.10.1 (abort with ^G) 1> [1024]. "Ѐ" 2> [1070,1085,1080,1082,1086,1076]. "Юникод" 3> [229,228,246]. "åäö" 4> <<208,174,208,189,208,184,208,186,208,190,208,180>>. <<"Юникод"/utf8>> 5> <<229/utf8,228/utf8,246/utf8>>. <<"åäö"/utf8>>
1> io:format("~tp~n",[{<<"åäö">>, <<"åäö"/utf8>>, <<208,174,208,189,208,184,208,186,208,190,208,180>>}]). {<<"åäö">>,<<"åäö"/utf8>>,<<"Юникод"/utf8>>}
  • erlang shell的交互Console也可以支持Unicode和输入和输出,在windows上首先要确认是否有合适的字体,如果没有可以使用DejaVu。在Unix系统下,需要检查Console是否支持Unicode,通过echo $LANG或者$LC_CTYPE来察看,还可以通过io:getopts()来检查当前Console使用的编码。

$ LC_CTYPE=en_US.ISO-8859-1

erl Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.10.1 (abort with ^G)

1> lists:keyfind(encoding, 1, io:getopts()).

{encoding,latin1}

2> q().

ok

$ LC_CTYPE=en_US.UTF-8

erl Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.10.1 (abort with ^G)

1> lists:keyfind(encoding, 1, io:getopts()).

{encoding,unicode}

2>
  • 不过虽然字符串变量能够被视为Unicode进行处理,但是erlang的代码还是被限制在ISO-latin-1字符集
2> Юникод.

* 1: illegal character
  • 接下来是关于erlang对文件命名不同编码的处理:

  目前的操作系统,都支持Unicode编码的文件命名,但不同的操作系统对文件命名编码有不同的约定,所以erlang也有不同的处理。对于windows和macos,所有文件命名的编码都强制性要求使用Unicode,macos要求UTF-8,虽然windows使用特殊的Unicode变种,但两者效果是相同。而Unix下面并不是强制要求使用Unicode编码,只是约定而已,那么在Unix下存在有多种编码的可能性,比如说一个文件名包含的字符串code point 属于128到255之间,可以编码成ISO-lation-1也可以编码成UTF-8(128以下的code point 两者是相兼容的,具体参看前面Unicode的编码介绍)。因此在windows和macos下,Erlang VM的默认行为是工作在"Unicode file name translation mode"下面,在这个模式下,所有的文件名都会以一个Unicode的list返回,并且自动转换成合适的编码传入到底层的文件系统。在Unix下没有自动打开这个模式,默认使用ISO-Latin-1,那么如果文件名编码是使用的UTF-8,那么就会返回“raw file names”,也就是一组包含编码的整型list.(比如使用list_dir_all/1函数时)我们可以通过在VM运行时,加入+fnu来打开"Unicode file name translation mode",默认下它会使用 latin1作为文件名编码,也可以使用file:native_name_encoding/0 来返回当前文件名使用的编码。

http://www.erlang.org/doc/apps/stdlib/unicode_usage.html

 


erlang Unicode 处理的更多相关文章

  1. [Erlang 0124] Erlang Unicode 两三事 - 补遗

    最近看了Erlang User Conference 2013上patrik分享的BRING UNICODE TO ERLANG!视频,这个分享很好的梳理了Erlang Unicode相关的问题,基本 ...

  2. Erlang的Unicode支持

    在R13A中, Erlang加入了对Unicode的支持.本文涉及到的数据类型包括:list, binary, 涉及到的模块包括stdlib/unicode, stdlib/io, kernel/fi ...

  3. [Erlang 0118] Erlang 杂记 V

       我在知乎回答问题不多,这个问题: "对你职业生涯帮助最大的习惯是什么?它是如何帮助你的?",我还是主动回答了一下.    做笔记 一开始笔记软件做的不好的时候就发邮件给自己, ...

  4. [Erlang 0108] Elixir 入门

    Erlang Resources里面关于Elixir的资料越来越多,加上Joe Armstrong的这篇文章,对Elixir的兴趣也越来越浓厚,投入零散时间学习了一下.零零散散,测试代码写了一些,Ev ...

  5. [Erlang 0107] Erlang实现文本截断

       抽时间处理一下之前积压的一些笔记.前段时间有网友 @稻草人 问字符串截断的问题"各位大侠 erlang截取字符串一般用哪个函数啊",有人支招用string:substr/3, ...

  6. Erlang 101 Erlang环境和顺序编程

    笔记系列 Erlang环境和顺序编程 Erlang并发编程 Erlang分布式编程 Yaws Erlang/OTP 日期              变更说明2014-10-12 A outline, ...

  7. erlang代码片段

    转载自http://blog.csdn.net/sw2wolf/article/details/6797708 .列表操作 lists:foreach(fun(X) -> io:format(& ...

  8. Windows下使用NIF扩展Erlang方法

    在Erlang中,NIF(Native Implemented Function)被用来扩展erlang的某些功能,一般用来实现一些erlang很难实现的,或者一些erlang实现效率不高的功能. N ...

  9. Erlang ODBC 处理中文

    erlang处理utf8字符集相对比较简单,因为它是用integer的list来保存所有的string的,所以处理什么字符集都没关系. 话虽这么说,但我在使用erlang的ODBC处理中文时,着实费了 ...

随机推荐

  1. mysql多表查询 查询排序

    有 ask 问题表  和 answer回答表  回答表中的ask_id和 ask表中的id对应 1.查询 /*查询回答了的 */select a.id,a.title,count(b.ask_id) ...

  2. Coursera公开课-Machine_learing:编程作业4

    编程作业: Neural Network Learning 源码上传到gitlab. 对于神经网络的理解也都在源码注释里面了,感兴趣可以看看.

  3. centos7安装mysql和mysql-connector-c++

    最近为了搭建自己的开发环境,又一次在centos7上面开始安装mysql和c++的访问环境,特此记录一下搭建过程,方便以后查阅 一.安装mysql centos7 默认安装了mariaDB,导致不能安 ...

  4. NetBeans将java项目编译成jar包

    1.找到file选项下的build.xml.

  5. (二)Python 学习第二天--爬5068动漫图库小案例

    (注:代码和网站仅仅是学习用途,非营利行为,源代码参考网上大神代码,仅仅用来学习

  6. (转)学习淘淘商城第二十二课(KindEditor富文本编辑器的使用)

    http://blog.csdn.net/u012453843/article/details/70184155 上节课我们一起学习了怎样解决KindEditor富文本编辑器上传图片的浏览器兼容性问题 ...

  7. 基于zk“临时顺序节点“的分布式锁

    import org.apache.zookeeper.*; import org.apache.zookeeper.data.Stat; import java.io.IOException; im ...

  8. Python-暑期实训day 1

    python基础: 一 编程语言 什么是编程语言? 上面提及的能够被计算机所识别的表达方式即编程语言,语言是沟通的介质,而编程语言是程序员与计算机沟通的介质.在编程的世界里,计算机更像是人的奴隶,人类 ...

  9. 在Unity中对注册表的信息进行操作

      问题1 在对注册表进行操作时无法生成注册表相关的类  解决办法:     增加头文件using Microsft.Win32; 问题2                    在运行程序时报错同时注 ...

  10. 16.1 foreach 循环中捕获变量的变化

    在 foreach 循环内的匿名函数(通常为Lambda表达式)中捕获循环 变量时要格外小心.代码清单16-1就展示了这样一个简单的示例,它看上去似乎会输出 x . y . z . string[] ...