许久没做erlang开发了,最近有网友问到erlang的问题,就抽时间看下。问题是这样的,模块有中文,将中文直接打印出来,shell下显示会出现乱码,但如果先将中文转成binary,就可以正常显示出来。
shell中文乱码问题
这里以一个简单的例子,说明下:
-module(m).
-compile(export_all).
test() ->
io:format("~ts~n", ["中国"]),
io:format("~ts~n", [list_to_binary("中国")]).
以R17之前的erlang版本编译,然后测试下结果:
Eshell V5.10.3 (abort with ^G)
1> c(m).
{ok, m}
2> m:test().
ä¸å½
中国
ok
打印下erlang汇编码,这个test函数实现如下:
{function, test, 0, 2}.
{label,1}.
{line,[{location,"erl.erl",4}]}.
{func_info,{atom,erl},{atom,test},0}.
{label,2}.
{allocate,0,0}.
{move,{literal,[[228,184,173,229,155,189]]},{x,1}}.
{move,{literal,"~ts~n"},{x,0}}.
{line,[{location,"erl.erl",5}]}.
{call_ext,2,{extfunc,io,format,2}}.
{move,{literal,[<<228,184,173,229,155,189>>]},{x,1}}.
{move,{literal,"~ts~n"},{x,0}}.
{line,[{location,"erl.erl",6}]}.
{call_ext_last,2,{extfunc,io,format,2},0}.
实际上,erlang在编译代码时会做优化。数据已知的话,list_to_binary在编译期就被优化掉了。
所以,test函数优化后如下:
test() ->
io:format("~ts~n", [[228,184,173,229,155,189]]),
io:format("~ts~n", [<<228,184,173,229,155,189>>]).
io:format/2 对中文的处理
看了 io:format/2 的实现代码,关键代码为以下两步:
1、格式化数据: io_lib:format/2
2、打印到shell:io:put_chars/1
现在用上面例子中的数据,
3> L = io_lib:format("~ts",[<<228,184,173,229,155,189>>]).
[[20013,22269]]
4> io:put_chars(L).
中国ok
这里分析io_lib:format/2 的代码,说说 ~ts 的处理过程。
%% io_lib.erl
format(Format, Args) ->
case catch io_lib_format:fwrite(Format, Args) of
{'EXIT',_} ->
erlang:error(badarg, [Format, Args]);
Other ->
Other
end.
实现代码在 io_lib_format模块,如下:
%% io_lib_format.erl
fwrite(Format, Args) when is_atom(Format) ->
fwrite(atom_to_list(Format), Args);
fwrite(Format, Args) when is_binary(Format) ->
fwrite(binary_to_list(Format), Args);
fwrite(Format, Args) ->
Cs = collect(Format, Args), %% 收集格式化信息,生成控制结构
Pc = pcount(Cs), %% 计算请求打印的数量
build(Cs, Pc, 0). %% 解析控制结构,生成数据
collect([$~|Fmt0], Args0) -> %% 格式化参数以 ~打头,否则忽略
{C,Fmt1,Args1} = collect_cseq(Fmt0, Args0),
[C|collect(Fmt1, Args1)];
collect([C|Fmt], Args) ->
[C|collect(Fmt, Args)];
collect([], []) -> [].
collect_cseq(Fmt0, Args0) ->
{F,Ad,Fmt1,Args1} = field_width(Fmt0, Args0),
{P,Fmt2,Args2} = precision(Fmt1, Args1),
{Pad,Fmt3,Args3} = pad_char(Fmt2, Args2),
{Encoding,Fmt4,Args4} = encoding(Fmt3, Args3),
{Strings,Fmt5,Args5} = strings(Fmt4, Args4),
{C,As,Fmt6,Args6} = collect_cc(Fmt5, Args5),
{{C,As,F,Ad,P,Pad,Encoding,Strings},Fmt6,Args6}.
%% 检查format 参数含有 t, 然后打标记 unicode,其他记latin1
encoding([$t|Fmt],Args) ->
true = hd(Fmt) =/= $l, %% 确保不是传入 ~tl
{unicode,Fmt,Args};
encoding(Fmt,Args) ->
{latin1,Fmt,Args}.
再看下以上build部分的代码。代码过长,做了删节:
%% io_lib_format.erl
build([{C,As,F,Ad,P,Pad,Enc,Str}|Cs], Pc0, I) ->
S = control(C, As, F, Ad, P, Pad, Enc, Str, I), %% 处理控制结构
Pc1 = decr_pc(C, Pc0),
if
Pc1 > 0 -> [S|build(Cs, Pc1, indentation(S, I))];
true -> [S|build(Cs, Pc1, I)]
end;
build([$\n|Cs], Pc, _I) -> [$\n|build(Cs, Pc, 0)];
build([$\t|Cs], Pc, I) -> [$\t|build(Cs, Pc, ((I + 8) div 8) * 8)];
build([C|Cs], Pc, I) -> [C|build(Cs, Pc, I+1)];
build([], _Pc, _I) -> [].
control($w, [A], F, Adj, P, Pad, _Enc, _Str, _I) ->
term(io_lib:write(A, -1), F, Adj, P, Pad);
control($p, [A], F, Adj, P, Pad, Enc, Str, I) ->
print(A, -1, F, Adj, P, Pad, Enc, Str, I);
control($W, [A,Depth], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(Depth) ->
term(io_lib:write(A, Depth), F, Adj, P, Pad);
control($P, [A,Depth], F, Adj, P, Pad, Enc, Str, I) when is_integer(Depth) ->
print(A, Depth, F, Adj, P, Pad, Enc, Str, I);
control($s, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_atom(A) ->
string(atom_to_list(A), F, Adj, P, Pad);
control($s, [L0], F, Adj, P, Pad, latin1, _Str, _I) -> %% 处理 ~s,如果数据标记是 latin1
L = iolist_to_chars(L0),
string(L, F, Adj, P, Pad);
control($s, [L0], F, Adj, P, Pad, unicode, _Str, _I) -> %% 处理 ~s,如果数据标记是 unicode
L = cdata_to_chars(L0),
uniconv(string(L, F, Adj, P, Pad));
control($e, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_float(A) ->
%% 该函数太长了,不是讨论重点,做了删节
cdata_to_chars([C|Cs]) when is_integer(C), C >= $\000 ->
[C | cdata_to_chars(Cs)];
cdata_to_chars([I|Cs]) ->
[cdata_to_chars(I) | cdata_to_chars(Cs)];
cdata_to_chars([]) ->
[];
cdata_to_chars(B) when is_binary(B) -> %% 如果数据是binary,做一下unicode转换
case catch unicode:characters_to_list(B) of
L when is_list(L) -> L;
_ -> binary_to_list(B)
end.
可想而知,如果没有不是 ~ts,或者不是binary,都不会做转换。
探讨乱码问题
回过头再看前面的乱码问题,相信不少人会有这3个疑问:
1、为什么会有乱码问题
2、为什么latin1能表示中文
3、为什么utf8保存的代码在shell下显示乱码
现在看下这3个问题:
1、为什么会有乱码问题
乱码问题的产生,是因为数据记录的字符集和显示的字符集不一样,就会有乱码问题。好比你用gbk记录,然后试图用utf8去读取。
那为何英文、数字没问题,就中文这些有问题呢?
这是因为,后来出现的字符集都在最早出现的标准字符集(7比特ASCII码)的基础上拓展,沿用了ASCII码对于英文、数字这些字符的编码设定。但是,对于拓展字符集每个语种都有自己的定义方式,同样一段字符数据用不同的字符集就有不同的解释。这就是乱码出现的原因。
所以,再后来就有unicode的出现,unicode 标准涵盖了世界上的所有字符、标点和符号,不论是哪个平台、程序或语言,unicode 都能够进行文本数据的处理、存储和交换。
2、那为什么latin1能表示中文?
在R17前,代码都是默认以latin1方式读取和编译的,生成的字符信息以 latin1形式保存。(这个与shell直接打印中文有本质区别)
实际上,使用latin1无法将中文解读出来,只是latin1读取数据时没有破坏原来的编码信息。如果原文以utf8记录,显示的时候又以utf8表示,就能正常显示。
3、为什么utf8保存的代码在shell下显示乱码
这是因为unicode和utf8是有区别的,shell使用unicode字符集,代码使用utf8保存,如果不做转换,直接显示就会有问题。正是这样,io_lib:format/2 也对编码做了特殊处理,但也局限于前面所述的情况。
顺便提一下,erlang最开始是使用latin1作为字符集,在R13A后开始支持unicode字符集。而对源代码的utf8编码支持,是R16A之后,同时支持以utf8读写文件。直到R17后,erlang才将utf8做为源代码的默认编码,在这之前,源代码都以latin1形式读取和编译的。R17如果想改变默认编码,方法就是在模块首行加 %% coding: latin-1
小结下,将utf8数据转换成unicode编码,方法如下:
utf8_list_to_string(List) ->
unicode:characters_to_list(list_to_binary(List)).
那读写utf8文件呢,怎么避免出现乱码呢?
read_and_write() ->
{ok,Bin} = file:read_file("file.txt"),
MyList = case unicode:characters_to_list(Bin) of
L when is_list(L) -> L;
_ -> binary_to_list(Bin)
end,
{ok,G} = file:open("new_file.txt",[write,{encoding,utf8}]),
io:put_chars(G,MyList),
file:close(G).
问题到这里就告一段落了,当然,erlang中文问题还不止如此,后续会继续讨论。好久没搞erlang了,突然心血来潮,写了这篇文章,希望喜欢。
2016/3/2 补充中文乱码问题的探讨
参考:
http://blog.csdn.net/mycwq/article/details/50762572
http://erlang.org/doc/apps/stdlib/unicode_usage.html
分享到:
相关推荐
要解决Erlang中的中文乱码问题,你需要确保以下几个方面都正确无误: 1. **文件编码**:确认你要处理的文件是以正确的编码(如UTF-8)保存的。可以使用诸如Notepad++之类的文本编辑器检查和转换文件编码。 2. **...
Erlang并发编程,Erlang程序设计,Erlang中文手册。 学习erlang的好资料。 Erlang是一个结构化,动态类型编程语言,内建并行计算支持。最初是由爱立信专门为通信应用设计的,比如控制交换机或者变换协议等,因此...
"Erlang中文基础教程" Erlang 编程语言是一种功能强大且灵活的编程语言,它提供了一个交互式的 shell 环境,允许用户在其中编写和运行代码。 Erlang Shell 是一个命令行交互环境,类似于 UNIX 和 LINUX 系统的...
### Erlang 中文基础教程:理解Erlang Shell与模块函数 #### 1. Erlang Shell:编程者的交互式环境 Erlang Shell是Erlang编程语言提供的一种交互式编程环境,允许开发者直接在命令行中编写、执行Erlang代码并观察...
**Erlang编程:Introducing Erlang** Erlang是一种函数式编程语言,由爱立信在1986年开发,主要用于构建高可用性、容错性和并发性的分布式系统。"Introducing Erlang"是Simon St. Laurent撰写的一本入门级教程,...
根据提供的文件内容,以下是对Erlang/OTP R11B中文手册内容的知识点汇总。 ### Erlang/OTP R11B中文手册概述 手册是一份关于Erlang/OTP R11B版本的官方文档的中文翻译,由Dino Wu翻译,邮箱为***和***,仅供学习...
通过阅读《Erlang程序设计中文版》并结合提供的完整书签,学习者可以逐步构建起对Erlang语言的全面理解,从而在实际工作中有效地运用Erlang解决复杂的问题。无论你是初学者还是有经验的开发者,这本书都能为你提供...
Erlang的设计为程序员提供了处理并发问题的强有力工具,使得程序能够以一种更接近真实世界交互的方式来模拟人类思考过程。 本书适合初学者入门和中级水平程序员进一步学习Erlang编程语言。它不仅包含顺序编程的基础...
这些特性使得Erlang在处理高并发和实时性问题上表现出色。 "erlang 中文,chm参考文档"提供了丰富的Erlang学习资源,包括"Erlang手册(5.8.2).chm"和"erlang深度分析.pdf"。这两个文件将帮助你全面理解Erlang的基础...
2. **API更新**:可能对Erlang的内置函数或模块进行增强,提供新的功能或修复已知问题。 3. **兼容性提升**:与先前版本相比,25.0可能增强了与其他软件或框架的兼容性。 4. **错误修复**:解决上一版本中的已知问题...
我自己在新学erlang,在LeetCode OJ上找了题目练习,题目很适合新手熟悉语言,但是LeetCode OJ里面只有几门主流语言的答案,下面是已完成的erlang源代码,后续有空再做其他问题续传,题目包含:(源码开头都有题目...
6. **性能优化**:通过解决实际问题,学习者可以学习到如何有效地利用Erlang的特性进行性能优化,比如避免全局状态,利用并行计算等。 综上所述,"xiandiao"文件夹中的源码是学习Erlang编程和理解其并发特性的宝贵...
Erlang/OTP 26.2.1,Erlang,OTP,26.2.1
Erlang是一种面向并发的、函数式编程语言,由瑞典电信设备制造商Ericsson为了实现分布式实时、高可靠性系统而开发。Erlang以其强大的并行处理能力、容错性和易于构建大规模分布式系统的特点,在电信、金融和互联网等...
对于想要学习Erlang的初学者,"Erlang中文手册"是极好的参考资料,提供了详细的语言特性和API文档。此外,“更多erlang资料下载.txt”可能包含更多学习路径和进阶教程,帮助开发者深入理解Erlang及其生态系统。
标题中提到的“Erlang官网下载过慢”可能是因为网络问题或者官方服务器的繁忙导致的,这对于急需安装或更新Erlang的开发者来说是一个常见问题。在这种情况下,用户可以选择通过第三方镜像站点或者从他人分享的安装包...