• Erlang ODBC 处理中文


    erlang处理utf8字符集相对比较简单,因为它是用integer的list来保存所有的string的,所以处理什么字符集都没关系。
    话虽这么说,但我在使用erlang的ODBC处理中文时,着实费了不少劲。

    说实话,erlang的ODBC不好用,现在也有一些直接使用数据库驱动的erlang库,但都不怎么成熟,项目里不太敢用。 还是用官方的ODBC踏实,而且换什么数据库都不用改代码,方便。

    开始时我以为既然数据库utf8的,我把erlang中二进制的utf8数据写到数据库表里就可以啦。后来发现,完全不是那么回事。erlang的ODBC并不是原封不动将数据写到表里,它会根据字段的类型进行修改。

    绕了好几圈,终于找到一种方法可以从表中读写中文啦。
    下面就说一下我的方法。

    测试的数据库:MYSQL,Oracle

    MySql字符集:

    SHOW VARIABLES LIKE 'character_set%';
    +--------------------------+----------------------------+
    | Variable_name            | Value                      |
    +--------------------------+----------------------------+
    | character_set_client     | utf8                       |
    | character_set_connection | utf8                       |
    | character_set_database   | utf8                       |
    | character_set_filesystem | binary                     |
    | character_set_results    | utf8                       |
    | character_set_server     | utf8                       |
    | character_set_system     | utf8                       |
    | character_sets_dir       | /usr/share/mysql/charsets/ |
    +--------------------------+----------------------------+

    Oracle字符集:

    SQL> select * from v$nls_parameters;
    +-----------------------------------------------------------------+-----------------------------------------------------------------+
    | PARAMETER                                                       | VALUE                                                           |
    +-----------------------------------------------------------------+-----------------------------------------------------------------+
    | NLS_LANGUAGE                                                    | AMERICAN                                                        |
    | NLS_TERRITORY                                                   | AMERICA                                                         |
    | NLS_CURRENCY                                                    | $                                                               |
    | NLS_ISO_CURRENCY                                                | AMERICA                                                         |
    | NLS_NUMERIC_CHARACTERS                                          | .,                                                              |
    | NLS_CALENDAR                                                    | GREGORIAN                                                       |
    | NLS_DATE_FORMAT                                                 | DD-MON-RR                                                       |
    | NLS_DATE_LANGUAGE                                               | AMERICAN                                                        |
    | NLS_CHARACTERSET                                                | AL32UTF8                                                        |
    | NLS_SORT                                                        | BINARY                                                          |
    | NLS_TIME_FORMAT                                                 | HH.MI.SSXFF AM                                                  |
    | NLS_TIMESTAMP_FORMAT                                            | DD-MON-RR HH.MI.SSXFF AM                                        |
    | NLS_TIME_TZ_FORMAT                                              | HH.MI.SSXFF AM TZR                                              |
    | NLS_TIMESTAMP_TZ_FORMAT                                         | DD-MON-RR HH.MI.SSXFF AM TZR                                    |
    | NLS_DUAL_CURRENCY                                               | $                                                               |
    | NLS_NCHAR_CHARACTERSET                                          | AL16UTF16                                                       |
    | NLS_COMP                                                        | BINARY                                                          |
    | NLS_LENGTH_SEMANTICS                                            | BYTE                                                            |
    | NLS_NCHAR_CONV_EXCP                                             | FALSE                                                           |
    +-----------------------------------------------------------------+-----------------------------------------------------------------+

    测试的数据库表:

    mysql:

    CREATE TABLE `t1` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `f1` varchar(45) DEFAULT NULL
    );

    oracle:

    create t1( "ID" NUMBER NOT NULL ENABLE,  "F1" VARCHAR2(45 CHAR));
    注意: VARCHAR2字段“F1”的units是CHAR,而且一定要是CHAR(默认为byte),为什么呢?后面会说


    1、连接数据库

    odbc:connect("DSN=test", [{scrollable_cursors, off},  {binary_strings, on}])

    注意,打开binary_strings,不打开应该也可以,但下面的测试代码需要做相应的修改。

    2、代码源文件格式

    如果在代码中有中文,需要将代码源文件保存为utf8格式,“并且”必须在代码文件的第一行加:
    %% -*- coding: utf-8 -*-

    3、插入中文字段

    3.1、使用odbc:sql_query(这个我没测试过)

    方法一:直接写中文

    odbc:sql_query(DB, "INSERT INTO t1(f1) VALUES('马天才');")
    

    ODBC返回出错如下:
    =ERROR REPORT==== 6-Aug-2013::16:29:09 ===
    ** Generic server <0.47.0> terminating 
    ** Last message in was {<0.46.0>,
                            {sql_query,[6,
                                        [73,78,83,69,82,84,32,73,78,84,79,32,116,
                                         49,40,102,49,41,32,86,65,76,85,69,83,40,
                                         39,39532,22825,25165,39,41,59]]},
                            infinity}
    ** When Server state == {state,#Port<0.758>,undefined,<0.46.0>,undefined,on,
                                   false,false,off,connected,undefined,0,
                                   [#Port<0.756>,#Port<0.757>],
                                   #Port<0.759>,#Port<0.760>}
    ** Reason for termination == 
    ** {{badmatch,{error,einval}},
        [{odbc,odbc_send,2,[{file,"odbc.erl"},{line,832}]},
         {odbc,handle_msg,3,[{file,"odbc.erl"},{line,557}]},
         {gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
         {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}

    看到39532,22825,25165这三个数字了吗?这是"马天才“这三个字的codepoints,应该就是这个原因。

    方法二,转为utf8试试

        F = <<"马天才"/utf8>>, %% <<233,169,172,229,164,169,230,137,141>>
        Sql = lists:flatten(io_lib:format("INSERT INTO t1(f1) VALUES('~s');",   [F])),
        odbc:sql_query(DB, Sql)

    这次插入是成功啦,但插入到数据库中是乱码。

    方法三,转为utf16(为什么要用utf16呢?有病乱投医)

        F = <<"马天才"/utf16-little>>, %% <<108,154,41,89,77,98>>
        Sql = lists:flatten(io_lib:format("INSERT INTO t1(f1) VALUES('~s');",   [F])),
        odbc:sql_query(DB, Sql)

    这次插入也成功啦,但select返回时是<<108,0,154,0,41,0,89,0,77,0,98,0>>,为什么加一个0呀
    数据库中看仍然是乱码。

    3.2 用odbc:param_query(这个测试是OK的)

    方法一:代码里直接中文

        D = <<"马天才"/utf16-little>>, %% 中文必须要转为utf16-little
        odbc:param_query(DB, "INSERT INTO t1(f1) VALUES(?);", [{{sql_wvarchar, 45}, [D]}])

    注意:1. 中文要用utf16-little,为什么? 看这里 http://www.erlang.org/doc/apps/odbc/databases.html#id61509
    2. 这里要用sql_wvarchar,而不是sql_varchar,否则与sql_query的结果一样。

    方法二:程序收到的utf8

     U = <<233,169,172,229,164,169,230,137,141>>, %% 这是utf8的"马天才“三个字
     D = unicode:characters_to_binary(U, utf8, {utf16, little}), %% 转换为utf16-little
     odbc:param_query(DB, "INSERT INTO t1(f1) VALUES(?);", [{{sql_wvarchar, 45}, [D]}])


    4、查询中文字段

     odbc:sql_query(DB, "select * from t1;")

    4.1 在mysql里返回正常

    返回的f1字段的值是<<108,154,41,89,77,98>>,是utf16-little的编码,
    需要传给其它程序显示时,需要将其转为utf8,方法就是:
    unicode:characters_to_binary(<<108,154,41,89,77,98>>, {utf16, little}, utf8)

    4.2 在oracle里有点问题

    如果定义的varchar2字段“F1”的units是byte,则会返回<<???>>,这就是为什么上面说一定要定义为char的原因。
    这是为什么呢?用erlang odbc看一下数据表的描述:  
    odbc:describe_table(DB, "t1")
    你会看到,mysql中是这样:
    [{"id",sql_integer},{"f1",{sql_wvarchar,45}}]
    

    oracle中如果varchar2为byte,则是这样:
    [{"ID",{sql_float,38}},{"F1",{sql_varchar,45}}]
    

    如果改为char,则是这样:
    [{"ID",{sql_float,38}},{"F1",{sql_wvarchar,45}}]
    

    也就是说,如果oracle中varchar2为byte,则erlang会认为它是sql_varchar类型,返回的结果不会转换。


  • 相关阅读:
    vue移动端适配问题
    excel 表格数据转json格式
    常用快捷键
    微信公众号监听返回事件
    总结css常用方法
    封装axios
    初学angular项目中遇到的一些问题
    jquery项目中一些常用方法
    怎样做ie兼容性
    vue事件修饰符
  • 原文地址:https://www.cnblogs.com/riskyer/p/3241056.html
Copyright © 2020-2023  润新知