【问题标题】:Python unicode string rejected by psycopgPython unicode 字符串被 psycopg 拒绝
【发布时间】:2016-11-15 23:30:26
【问题描述】:

我收到了一个来自野外的 unicode 字符串,导致我们的一些 psycopg2 语句失败。

我已将问题简化为 SSCE:

import psycopg2
conn = psycopg2.connect(...)
cur = conn.cursor()
x = u'\ud837'
cur.execute("SELECT %s", (x,))
print cur.fetchone()

运行它会产生以下异常:

Traceback (most recent call last):
  File ".../run.py", line 65, in <module>
    cur.execute("SELECT %s AS test", (x,))
psycopg2.DataError: invalid byte sequence for encoding "UTF8": 0xed 0xa0 0xb7

根据一些 cmets,很明显这个特殊角色是代理对的一半,因此无法独立生活。

具体来说,我正在寻找一种机制来检测 Python 2 中字符串何时包含不完整的代理对。

我发现一种导致异常的方法是尝试x.encode('utf16').decode('utf16'),但是,由于我并不完全了解相关的风险,所以在这里我会有些担心。

编辑:将 SSCE 字符串缩减为导致问题的单个字符,添加了基于 cmets 的信息。

【问题讨论】:

  • 该字符代表one half of a surrogate pair,并不代表它自己的代码点。大概您是通过一个 API 获得的,该 API 将一个 UTF-16 编码的字符串拆分,而不注意字符边界。
  • @user4815162342 那么如何检测python中的给定字符串是否包含任何此类不完整的代理对?
  • 只是好奇,我的回答对这个问题有帮助吗?

标签: python postgresql unicode psycopg2


【解决方案1】:

字符串u'\ud837'surrogate pair 的一个单独成员组成,两个物理字符按顺序出现以形成一个逻辑字符。因此,它没有定义 Unicode 代码点 - 相反,它是 UTF-16 编码的实现细节,使用它将完整的代码点范围打包成 16 位代码单元。 Python 3 正确拒绝以任何字节编码(包括 UTF-* 变体)对单独代理进行编码的尝试。

该字符串可能源自一个内部使用 UTF-16 的系统(例如 Java、C#、Windows 或使用 16 位 Py_UNICODE 构建的 Python 2),该系统天真地缩短了字符串而不考虑代理项。

this answer 获取正则表达式,应该可以使用以下代码有效地检测此类字符串:

import re

lone = re.compile(
    ur'''(?x)            # verbose expression (allows comments)
    (                    # begin group
    [\ud800-\udbff]      #   match leading surrogate
    (?![\udc00-\udfff])  #   but only if not followed by trailing surrogate
    )                    # end group
    |                    #  OR
    (                    # begin group
    (?<![\ud800-\udbff]) #   if not preceded by leading surrogate
    [\udc00-\udfff]      #   match trailing surrogate
    )                    # end group
    ''')

def invalid_unicode(s):
    assert isinstance(s, unicode)
    return lone.search(s) is not None

【讨论】:

    【解决方案2】:

    要检测字符串无效utf-8,只需在psycopg2 中执行之前尝试将其编码在try/except 中。

    至于导致问题的原因,在utf-16编码的字符串中间有一个特定的字符:\U000d8a85。所以不是 Postgres 不考虑utf-8,其实不是。

    【讨论】:

    • 感谢您的解释,但x.encode('utf-8') 不会导致异常。 x.encode('utf-8').decode('utf-8') 也没有。这让我相信:python 认为这是有效的 utf-8,或者 python 具有以非严格方式解码 utf-8 的后备。
    • 另外,经过进一步修改,导致问题的特定字符似乎是\ud837 - 知道那里发生了什么吗?
    猜你喜欢
    • 1970-01-01
    • 2017-11-19
    • 2011-11-12
    • 2021-11-03
    • 2016-01-12
    • 1970-01-01
    • 1970-01-01
    • 2011-11-07
    • 2012-04-21
    相关资源
    最近更新 更多