Python psycopg 拒绝了 unicode 字符串
Python unicode string rejected by psycopg
我收到了一个来自野外的 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
根据一些评论,很明显这个特定角色是代理对的一半,因此无法独立存在。
具体来说,我正在寻找一种机制来检测字符串何时包含 Python 2.
中的不完整代理项对
我发现的一种导致异常的方法是尝试 x.encode('utf16').decode('utf16')
,但是,由于我不完全理解相关风险,所以我在这里有点担心。
编辑:将导致问题的 SSCE 字符串减少为单个字符,根据评论添加了信息。
要检测字符串无效 utf-8
,只需在 psycopg2
.
中执行之前将其编码尝试包装在 try/except
中
至于是什么原因造成的,在utf-16
编码的字符串中间有一个特定的字符:\U000d8a85
。所以不是Postgres不考虑它utf-8
,它真的不是。
字符串u'\ud837'
由surrogate pair的一个单独成员组成,两个物理字符依次出现形成一个逻辑字符。因此,它不定义 Unicode 代码点 - 相反,它是 UTF-16 编码的实现细节,使用它将完整的代码点范围打包成 16 位代码单元。 Python 3 正确拒绝尝试以任何字节编码(包括 UTF-* 变体)对单独代理项进行编码。
该字符串可能源自内部使用 UTF-16 的系统(例如 Java、C#、Windows 或 Python 2 使用 16 位 Py_UNICODE
) 天真地缩短了字符串而不考虑代理项。
使用 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
我收到了一个来自野外的 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
根据一些评论,很明显这个特定角色是代理对的一半,因此无法独立存在。
具体来说,我正在寻找一种机制来检测字符串何时包含 Python 2.
中的不完整代理项对我发现的一种导致异常的方法是尝试 x.encode('utf16').decode('utf16')
,但是,由于我不完全理解相关风险,所以我在这里有点担心。
编辑:将导致问题的 SSCE 字符串减少为单个字符,根据评论添加了信息。
要检测字符串无效 utf-8
,只需在 psycopg2
.
try/except
中
至于是什么原因造成的,在utf-16
编码的字符串中间有一个特定的字符:\U000d8a85
。所以不是Postgres不考虑它utf-8
,它真的不是。
字符串u'\ud837'
由surrogate pair的一个单独成员组成,两个物理字符依次出现形成一个逻辑字符。因此,它不定义 Unicode 代码点 - 相反,它是 UTF-16 编码的实现细节,使用它将完整的代码点范围打包成 16 位代码单元。 Python 3 正确拒绝尝试以任何字节编码(包括 UTF-* 变体)对单独代理项进行编码。
该字符串可能源自内部使用 UTF-16 的系统(例如 Java、C#、Windows 或 Python 2 使用 16 位 Py_UNICODE
) 天真地缩短了字符串而不考虑代理项。
使用 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