Erlang正则表达式匹配汉字

时间:2017-10-13 17:55:21

标签: character-encoding erlang

TL; DR:

25> re:run("йцу.asd", xmerl_regexp:sh_to_awk("*.*"), [{capture, none}]). 
** exception error: bad argument
     in function  re:run/3
        called as re:run([1081,1094,1091,46,97,115,100],
                         "^(.*\\..*)$",
                         [{capture,none}])

如何使这项工作?显然,'йцу'是不属于拉丁字符集的字符;有没有办法告诉re模块或整个系统使用不同的“字符串”字符集运行?

原始问题(记录):

另一个“编程Erlang”问题)

在第16章中有一个关于从mp3文件中读取标签的例子。它很有效。但是,在提供的模块 lib_find 中似乎存在一些错误,该模块具有搜索路径以匹配文件的功能。这是有效的电话:

61> lib_find:files("../..", "*.mp3", true).   
["../../early/files/Veronique.mp3"]

并且此调用失败:

62> lib_find:files("../../..", "*.mp3", true).
** exception error: bad argument
     in function  re:run/3
        called as re:run([46,46,47,46,46,47,46,46,47,46,107,101,114,108,47,98,117,
                          105,108,100,115,47,50,48,46,49,47,111|...],
                         "^(.*\\.mp3)$",
                         [{capture,none}])
     in call from lib_find:find_files/6 (lib_find.erl, line 29)
     in call from lib_find:find_files/6 (lib_find.erl, line 39)
     in call from lib_find:files/3 (lib_find.erl, line 17)

具有讽刺意味的是,调查导致在Erlang自己的安装中找到了罪魁祸首:

  

.kerl /建立/ 20.1 / otp_src_20.1 / LIB / SSH /测试/ ssh_sftp_SUITE_data / sftp_tar_test_data_高兴

好吧,这似乎意味着Erlang正在使用更严格的默认字符集,其中不包括hànzì。有什么选择?显然,我可以忽略这一点并继续我的学习,但我觉得我可以从这一个中学到更多东西=)例如 - 我在哪里/如何修复默认的字符集?我有点惊讶它默认是UTF8以外的东西 - 所以也许我的轨道错了?

谢谢!

1 个答案:

答案 0 :(得分:3)

<强> TL; DR:

通过使用选项unicode将正则表达式模式置于unicode模式,可以访问UTF-8正则表达式。 (请注意,字符串"^(.*\\..*)$"是您调用xmerl_regexp:sh_to_awk/1的结果。)

1> re:run("なにこれ.txt", "^(.*\\..*)$").
** exception error: bad argument
     in function  re:run/2
        called as re:run([12394,12395,12371,12428,46,116,120,116],"^(.*\\..*)$")
2> re:run("なにこれ.txt", "^(.*\\..*)$", [unicode]).
{match,[{0,16},{0,16}]}

从您的确切示例:

11> re:run("йцу.asd", "^(.*\\..*)$", [unicode, {capture, none}]).       
match

或者

12> {ok, Pattern} = re:compile("^(.*\\..*)$", [unicode]).
{ok,{re_pattern,1,1,0,
                <<69,82,67,80,87,0,0,0,16,8,0,0,65,0,0,0,255,255,255,
                  255,255,255,...>>}}
13> re:run("йцу.asd", Pattern, [{capture, none}]).               
match

re的文档非常冗长,但这是因为正则表达式本身就是一个复杂的主题。您可以在re:compile/2的文档中找到已编译的正则表达式的选项,以及在re:run/3的文档中运行的选项。

<强>讨论

Erlang已经确定了字符串虽然仍然是代码点列表,但都是UTF-8 everywhere。当我在日本工作并且一直处理这个问题时,这对我来说是一个很大的缓解,因为我可以停止使用我过去需要的大约一半的转换库(yay!),但是有点复杂的事情对于string模块的用户,因为many operations there now perform under slightly different assumptions(字符串仍然被认为是“平坦的”,即使它是字形集群的深层列表,只要这些集群存在于列表的第一级)

不幸的是,编码并不是一件容易处理的事情,一旦你走出最常见的表现形式,UTF-8就不那么简单 - 这是一项正在进行的工作。不过,我可以放心地告诉你,处理二进制,字符串,深层列表和io_data()表单中的UTF-8数据,无论是文件名,文件数据,网络数据,还是来自WX或Web表单的用户输入一旦你阅读了unicode,正则表达式和字符串文档,就会按预期工作。

但那当然是很多熟悉的东西。如果您使用unicode:characters_to_list/1unicode:characters_to_binary/1将所有来自外部的内容解码为UTF-8,并且将二进制字符串指定为utf8 binary types,那么99%的时间会按预期工作:

3> UnicodeBin = <<"この文書はUTF-8です。"/utf8>>.
<<227,129,147,227,129,174,230,150,135,230,155,184,227,129,
  175,85,84,70,45,56,227,129,167,227,129,153,227,128,130>>
4> UnicodeString = unicode:characters_to_list(UnicodeBin).  
[12371,12398,25991,26360,12399,85,84,70,45,56,12391,12377,
 12290]
5> io:format("~ts~n", [UnicodeString]).
この文書はUTF-8です。
ok
6> re:run(UnicodeString, "UTF-8", [unicode]).
{match,[{15,5}]}
7> re:run(UnicodeBin, "UTF-8", [unicode]).   
{match,[{15,5}]}
8> unicode:characters_to_binary(UnicodeString).
<<227,129,147,227,129,174,230,150,135,230,155,184,227,129,
  175,85,84,70,45,56,227,129,167,227,129,153,227,128,130>>
9> unicode:characters_to_binary(UnicodeBin).   
<<227,129,147,227,129,174,230,150,135,230,155,184,227,129,
  175,85,84,70,45,56,227,129,167,227,129,153,227,128,130>>