Unicode基础
编码方式
定义字符集中每个字符的codepoint(数字编码)
- ASCII: 不用多说,编码空间为7位(0-127)
- ISO 8859-1: 又称Latin-1,以ASCII为基础,在空置的0xA0-0xFF的范围内,加入96个字母及符号。编码空间为8位(0-255)
- UCS-2: 16位编码空间 又称基本多文种平面或零平面
- UCS-4: 32位编码空间 在UCS-2基本上,加入辅助平面(目前有16个辅助平面,至少需要21位)
- 注1: UCS(Universal Character Set, 通用字符集)
- 注2: 以上四种编码都是向前兼容的,通常我们所说的Unicode编码指UCS-2和UCS-4,目前广泛运用的是UCS-2
实现方式
实现方式将字符的数字编码存储在计算机字节中,由于节省空间和平台差异性等,衍生不同的实现方式
- UTF-8: 一种变长编码,使用1-3个字节编码UCS-2字符集,1-6个字节可编码UCS-4字符集(目前只用最多四个字节即可表示UCS-4所定义的17个平面)。优点是兼容ASCII,节省空间,并且不存在字节序的问题
- UTF-16: 和UTF-8类似,使用2个字节来编码UCS-2字符集(UCS-2中有预留的位用于实现UTF-16扩展多字节),使用4个字节来编码UCS-4字符集。由于使用两个字节作为基本编码单位,UTF-16存在字节序的问题,通常使用BOM来解决
- UTF-32: 32位定长编码,能够表示UCS-4字符集所有字符,但空间占用大,因此很少见
- 注1: UTF(Unicode Transformation Format, Unicode转换格式)
- 注2: BOM(byte-order mark, 字节顺序标记)
Erlang中的Unicode
Unicode表示
%% 环境 Mac OSX Yosemite & Erlang OTP/19
Eshell V8.1 (abort with ^G)
1> L = "中文".
[20013,25991] % Erlang lists存放的是字符的Unicode编码
2> B = <<"中文">>.
<<45,135>> % Erlang只知"中文"的Unicode编码[20013,25991],并不知应该用何种实现方式(UTF8或其他),默认它会将Unicode编码 rem 256,产生0-255间的编码(并按照Lantin-1解码)
% 下面我们将考虑将"中文"转换为binary
% 方案一. erlang:list_to_binary -> error
3> list_to_binary(L). % 该函数支持的list只能是iolist(见后面术语参考),否则Erlang并不知道你想将字符串转换为何种编码格式的binary
** exception error: bad argument
in function list_to_binary/1
called as list_to_binary([20013,25991])
% 方案二. unicode:characters_to_binary -> ok
4> UTF8 = unicode:characters_to_binary(L).% 将L中的unicode编码转换为UTF8 binary
<228,184,173,230,150,135>>
5> UTF16Big = unicode:characters_to_binary(UTF8,utf8,utf16).
<<78,45,101,135>> % 默认为Big Endian
6> UTF16Little = unicode:characters_to_binary(UTF8,utf8,{utf16,little}).
<<45,78,135,101>>
% 方案三. 利用binary构造语法构建
7> UTF8 = <<"中文"/utf8>>.
<<228,184,173,230,150,135>>
8> UTF8 = <<L/utf8>>. % Why ?
** exception error: bad argument
在Erlang中,字符串就是整数列表,并且这个整数可以无限大,lists将保存其中每个字符的Unicode编码,只要lists中的整数是有效的Unicode codepoint,就可以找到对应的字符。因此也就不存在UTF8/UTF16格式的lists字符串一说。而binary的处理则要麻烦一些,Erlang用UTF8作为Unicode在binary上的实现方式,unicode模块提供了这方面丰富的unicode编码处理接口。
Unicode使用
8> io:format("~s", [L]).
** exception error: bad argument
in function io:format/3
called as io:format(<0.50.0>,"~s",[[20013,25991]])
9> io:format("~p", [L]).
[20013,25991]ok
10> io:format("~ts", [L]).
中文ok
11> io:format("~s", [UTF8]).
ä¸æok
12> io:format("~p", [UTF8]).
<<228,184,173,230,150,135>>ok
13> io:format("~ts", [UTF8]).
中文ok
先解释几个Erlang术语:
- iolist: 0-255编码(Latin-1)的lists,binary,或它们的嵌套,如
[["123",<<"456">>],<<"789">>]
- unicode binary: UTF8编码的binary(Erlang默认使用UTF8 binary编码unicode)
- charlist: UTF8编码的binary,或包含有效unicode codepoint的lists,或它们的嵌套,如
[<<"hello">>, "中国"]
~s
只能打印iolist,binary,或atom,因此不能直接打印中文lists(无法解码超过255的codepoint)或UTF8 binary(会按字节解释,出现乱码)。
~ts
则可打印charlist和unicode binary。
~p
如果不能打印出ASCII(0-127)字符,则直接打印出原生Term,不会对Unicode编码进行处理。
参考: