从PostgreSQL Erlang获取unicode数据

时间:2014-06-09 12:08:22

标签: postgresql unicode erlang ejabberd

我正在尝试使用Erlang从PostgreSQL中获取数据。 这是我从DB获取数据的代码。但是我在'status'栏中有cyrrilic数据。这个cyrrilic数据没有正确获取。 我尝试使用UserInfo = io_lib:format("~tp ~n",[UserInfoQuery]),,但这似乎不起作用,因为它会崩溃应用程序。

UserInfoQuery = odbc_queries:get_user_info(LServer,LUser),
UserInfo = io_lib:format("~p",[UserInfoQuery]),
?DEBUG("UserInfo: ~p",[UserInfo]),
StringForUserInfo = lists:flatten(UserInfo),

get_user_info(LServer, Id) ->
ejabberd_odbc:sql_query(
  LServer,
  [<<"select * from users "
     "where email_hash='">>, Id, "';"]).

这是从DB

获取的数据
{selected,[<<"username">>,<<"password">>,<<"created_at">>,
           <<"id">>,<<"email_hash">>,<<"status">>],
          [{<<"admin">>,<<"admin">>,<<"2014-05-13 12:40:30.757433">>,
            <<"1">>,<<"adminhash">>,
            <<209,139,209,132,208,178,208,176,209,139,209,132,208,
              178,208,176>>}]}

问题:

  1. 如何从列中提取数据?例如,仅来自的数据 'status'栏?
  2. 如何从数据库中提取unicode数据?我应该从数据库中获取数据然后使用 io_lib:格式(“~tp~n”)就可以了吗?有没有更好的方法呢?
  3. 其他问题:有没有办法以人类可读的格式获取字符串,以便来自RowUnicode的StringForUserInfo = 'ыфваыфва'? 我试过这个:

    {selected, _, [Row]} = UserInfoQuery,
    RowUnicode = io_lib:format("~tp~n", [Row]),
    ?DEBUG("RowUnicode: ~p",[RowUnicode]),
    StringForUserInfo = lists:flatten(RowUnicode),
    

    错误:

    bad argument in call to erlang:iolist_size([123,60,60,34,97,100,109,105,110,34,
    62,62,44,60,60,34,97,100,109,105,110,34,62,62,44,60,60,34,50,...])
    

2 个答案:

答案 0 :(得分:2)

Erlang ODBC驱动程序从数据库中完美地获取了状态列。实际上,PostgreSQL对您的数据进行编码是UTF-8,您获得的值是UTF-8编码。

Status = <<209,139,209,132,208,178,208,176,209,139,209,132,208,178,208,176>>.

这是一个表示UTF-8中字符串ыфваыфва的二进制文件。 您可以在代码中直接使用UTF-8编码的二进制文件。如果要使用unicode字符点而不是UTF-8字节,可以将其转换为整数列表(Erlang用语中的字符串)。只需使用unicode:characters_to_list/1,在您的情况下将生成列表[1099,1092,1074,1072,1099,1092,1074,1072]。这是相同字符串的列表表示。 Unicode字符1099(十六进制为16#044B)是ы(CYRILLIC SMALL LETTER YERU,cf Cyrillic excerpt unicode chart)。

Erlang可以在上面的两个表示中处理unicode文本:unicode字符列表作为整数和UTF-8编码字符的二进制文件。

让我们来看一个较小的例子,字符串"ы"。该字符串由unicode字符044B CYRILLIC SMALL LETTER YERU组成,可以编码为<<209,139>>的二进制文件或[16#044B](= [1099])的列表。

历史上,整数列表和二进制文件都是Latin-1(ISO-8859-1)编码。 Unicode和ISO-8859-1具有0到255之间相同的值,但UTF-8转换仅匹配0-127范围内字符的ISO-8859-1。出于这个原因,Erlang的~s格式参数有一个unicode转换修饰符~ts。以下行将无法按预期工作:

io:format("~s", [<<209,139>>]).

它将输出两个字符,00D1(LATIN CAPITAL LETTER N WITH TILDE)和008B(PARTIAL LINE FORWARD)。这是因为&lt;&lt;&lt;&lt;&lt;&nbsp;&gt;&gt;&gt;被解释为Latin-1字符串而不是UTF-8编码字符串。

以下行将失败:

io:format("~s", [[1099]]).

这是因为[1099]不是有效的Latin-1字符串。

相反,你应该写:

io:format("~ts", [<<209,139>>]),
io:format("~ts", [[1099]]).

Erlang的~p格式参数也有一个unicode翻译修饰符~tp。但是,~tp将不会执行您正在寻找单独的内容。无论您使用~p还是~tp,默认情况下,io_lib:format/2都会将上面的状态UTF-8编码二进制格式设置为:

<<209,139,209,132,208,178,208,176,209,139,209,132,208,178,208,176>>

实际上,t修饰符仅表示参数应接受unicode输入。如果您使用~p,在格式化字符串或二进制文件时,Erlang将确定是否可以将其表示为Latin-1字符串,因为输入可能是Latin-1编码。这种启发式方法允许Erlang在大多数情况下正确区分整数和字符串列表。要查看工作中的启发式,您可以尝试以下方法:

io:format("~p\n~p\n", [[69,114,108,97,110,103], [1,2,3,4,5,6]]).

启发式检测到[69,114,108,97,110,103]实际上是"Erlang",而[1,2,3,4,5,6]只是一个整数列表。

如果你使用~tp,Erlang会希望字符串或二进制文件是unicode编码的,然后应用默认标识启发式。目前默认的启发式(R17)也是latin-1。由于您的字符串无法用Latin-1表示,因此Erlang会将其显示为整数列表。幸运的是,您可以通过在命令行上将+pc unicode传递给Erlang来切换到Unicode启发式算法,这将产生您正在寻找的内容。

$ erl +pc unicode

因此,解决问题的方法是通过+pc unicode并使用~tp

答案 1 :(得分:1)

我不明白为什么io:format("~tp")不起作用,但您可以提取所需的行和列,并使用io:format("~ts")进行打印:

> {selected, _, [Row]} = UserInfoQuery.
> io:format("~ts~n", [element(6, Row)]).
ыфваыфва
ok