是否有一种非启发式的方法来查找字符串的编码(即列表)?

Is there a non-heuristic way of finding the encoding of a string (ie. list)?

例如,对于 IoDevices,可以使用 io:getopts/1,但我找不到任何用于纯字符串的方法。

例如,

ManPage = os:cmd("man ls").           
%   [76,83,40,49,41,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
%   32,32,32,32,32,32,32,32,32,32|...]

io:format("~p~n",[ManPage]).         
%   [76,83,40,49,41,(...)

io:format("~ts~n",[ManPage]).
%   LS(1)                   User Commands         LS(1)
%   NAME
%          ls - list directory contents

文档 on using Unicode in Erlang 仅提及启发式方法,但它可能已过时,因为根据示例 io_lib:format/2~ts 控制字符生成 utf-8 输出。尝试使用 Erlang 18.0:

Bullet = "\x{2022}".
%   [8226]

io:format("~ts~n", [Bullet]).
%   •
%   ok
io:format("~ts~n", ["•"]).   
%   •
%   ok

io_lib:format("~ts~n", [Bullet]).
%   [[8226],"\n"]

我知道我可以使用 unicode:characters_to_binary/{1,2,3},因为它接受 latin1 或 utf8 编码输入并吐出 unicode 编码输出,但我很好奇是否还有其他方法。

有趣的是,unicode:characters_to_binary/1 工作正常而 unicode:characters_to_list/1 没有(或者我误用了它)。

unicode:characters_to_binary(ManPage).                       
%   <<"LS(1)   User Commands   LS(1)\n\n\n\nNAME\n  "...>>

unicode:characters_to_list(ManPage).  
%   [76,83,40|...]

unicode:characters_to_list(ManPage, latin1).
%   {error,"LS(1)   User Commands  LS(1",
     [8208,10,32|...]}

不幸的是,只有启发式方法可以确定字符编码。有一个简短的解释为什么 here.

就是说,在您在上面指定的特定情况下,真正的问题是系统(不是 Erlang)shell 设置的编码方式。我们可以通过直接检查环境来发现这一点(虽然这将是一个有点特定于平台的解决方案——我是从一个使用 Bash 的 Debian 派生系统写的):

1> LANG = os:cmd("echo $LANG").
"ja_JP.UTF-8\n"
2> {_, Enc} = lists:split(6, LANG).
{"ja_JP.","UTF-8\n"}
3> Encoding = string:strip(Enc, right, $\n).
"UTF-8"

然而,这是一个相当糟糕的解决方案。它是完全不可移植的,并且不能保证您的环境确实遵循规则并将 5 个字符 language/region,然后是一个点,然后是编码放入其 $LANG 环境变量中。我很确定这不起作用,例如,至少在某些版本的 Solaris 和 AIX 上,我认为获得编码的方法是检查 $LC_CTYPE 或类似的东西(或者可能是倒退的) ...或者...看,我什至不记得这个的怪癖这一事实足以表明这是不可靠的)。

另一种方法是使用 locale 命令并让它直接为您提供字符集:

4> os:cmd("locale charmap").
"UTF-8\n"

后面的换行符让我很烦,所以...

5> string:strip(os:cmd("locale charmap"), right, $\n).
"UTF-8"

也就是说,locale 命令也并非随处存在。在任何情况下,检查来自环境和环境变量的语言环境输出数据的某种组合应该可以解决问题,尽管要使其可移植,您需要使用几种方法来武装您的系统。幸运的是 大多数 系统现在默认是 utf8,除了 Windows,但至少 Windows 在内部是 mostly标准化。

(如果您专门处理手册页...请记住,手册页中嵌入了用于标记的控制字符,因此虽然手册页的纯文本输出符合您的期望, man 解释的实际联机帮助页数据已标记。根据您的操作,直接操作联机帮助页存档数据可能更容易。)

不清楚我们在说什么编程语言,但是没有这样的方法。

您可能会要求用户设置环境变量 LC_ALL/LC_CTYPE/LANG 以匹配数据,但不能确定他们是否真的这样做了。

此外,您的程序可能必须在同一个程序中处理不同的编码-运行(例如来自不同的文件),因此您要么使用一些启发式算法来猜测编码(预计会有麻烦!),或者为用户提供一种指定编码的方法——就像文本编辑器在您打开文件时所做的那样。