【问题标题】:Unable to save a specific special character (ü) in Oracle database无法在 Oracle 数据库中保存特定的特殊字符 (ü)
【发布时间】:2021-06-21 10:48:08
【问题描述】:

我正在尝试通过 Python 2 (Flask) API 从 Python 3 (Django) 应用程序中获取特殊字符(例如:ñ、ü、é)并进入 Oracle 数据库(Oracle 19c Enterprise)。这取代了可以直接访问数据库并能够毫无问题地保存特殊字符的 Java 应用程序。

为了使用特殊字符进行 API 调用,我发现我必须对值进行 URL 编码:

>>> comment = "Joëlle Küsel Núñez-Chaillüé"
>>> urllib.parse.quote(comment).encode('utf-8')
b'Jo%C3%ABlle%20K%C3%BCsel%20N%C3%BA%C3%B1ez-Chaillu%CC%88%C3%A9'

该字符串被发送到 Python 2 API。我最初的尝试涉及在将其发送到 Oracle 之前在 Python 2 中对其进行解码,但是,任何将值传递给 cx_Oracle 的尝试都会导致 'ascii' codec can't encode characters in position 3-4: ordinal not in range(128)

我试图通过简单地将 URL 编码的字符串直接传递给 Oracle 来解决它:

>>> comment_blob = cx.cursor().var(cx_Oracle.BLOB)
>>> comment_blob.setvalue(0, bytes(u'{}'.format(comment.decode('utf-8'))))

在 Oracle 中,我尝试解码该值:

v_comment := UTL_RAW.CAST_TO_VARCHAR2( parameter );
v_comment := convert(utl_url.unescape(v_comment), 'WE8ISO8859P15');

这适用于除 ü 之外的所有内容,在某些情况下会被其后的字符覆盖(但在粘贴到任何其他文本编辑器时正确显示),有时会更改为完全不同的字符。例如:

Joëlle Küsel Núñez-Chaillüé
Joëlle Kÿsel Núñez-Chaillüé

字符 9 从 ü 更改为 ÿ,虽然最后两个字符在这里看起来正确,但在 SQL Developer 中,它们被组合成一个字符(通过旧 Java 应用插入的值在 SQL Developer 中正确显示)。

任何想法为什么我只有 ü 字符有问题?

【问题讨论】:

    标签: python oracle plsql special-characters cx-oracle


    【解决方案1】:

    如果您有较旧的 cx_Oracle 模块,则需要在创建连接时指定字符集,例如:

    connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1", encoding="UTF-8")
    

    如果升级到 cx_Oracle 8,那么这个字符集是默认的,所以encoding 选项可以省略。见Setting the Client Character Set

    另外两点:

    【讨论】:

    • 在连接时指定编码并没有解决我的问题。我们拥有的 Python 2 API 网关不是我可以控制的,但我相信人们正在努力将其转换为 Python 3。我确实设法想出了一个解决方法,即使它是我也会发布的答案不是一个很好的解决方案。感谢您的帮助!
    【解决方案2】:

    我想出了一个可行的(虽然不是理想的)变通办法来解决我的问题。在我的 Python 3 (Django) 应用程序中,我将字符串转换为字符代码列表。我通过 Python 2 API 传递代码列表而不对其进行任何操作,然后使用 PL/SQL 将其转换回常规字符。

    Django 代码:

    comment = str([ord(c) for c in comment]).strip('[]').replace(' ', '')
    
    # Send comment to API as a parameter
    

    Python 2 API:

    # Call PL/SQL function passing in the list of character codes as a string
    
    cursor.callfunc(
        'save_comment', cx_Oracle.STRING, [comment]
    )
    
    

    甲骨文:

    FUNCTION save_comment(p_comment VARCHAR2)
        RETURN VARCHAR2
    IS
        v_comment           VARCHAR2(500) := '';
        v_remain            VARCHAR2(500);
        v_char              VARCHAR2(3);
        v_char_code         NUMBER;
    BEGIN
    
        -- Comment comes in as list of character codes
        -- '74,111,235,108,108,101,32,75,252,115,101,108,32,78,250,241'
        v_remain := p_comment;
    
        WHILE v_remain IS NOT NULL LOOP
    
            -- Pop the first character code from the list
            IF v_remain LIKE '%,%' THEN
                v_char := substr(v_remain, 1, instr(v_remain, ',')-1);
                v_remain := substr(v_remain, instr(v_remain, ',') + 1);
            ELSE
                v_char := v_remain;
                v_remain := NULL;
            END IF;
    
            -- Turn character code from VARCHAR2 into NUMBER
            v_char_code := to_number(v_char);
    
            -- Convert Python char code to Oracle char code
            IF v_char_code >= 192 THEN
                v_char_code := v_char_code + 49856;
            ELSIF v_char_code > 160 THEN
                v_char_code := v_char_code + 49664;
            END IF;
    
            -- Add this character to the comment
            v_comment := v_comment || chr(v_char_code);
        END LOOP;
    
        RETURN v_comment;
    END;
    

    这个方便的 ASCII 字符代码列表是一个很好的资源:
    https://www.techonthenet.com/ascii/chart.php

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-06
      • 1970-01-01
      • 1970-01-01
      • 2015-04-06
      • 1970-01-01
      • 2021-06-14
      • 1970-01-01
      • 2015-04-19
      相关资源
      最近更新 更多