2016-11-14 2 views
2

Я получил строку unicode из дикой природы, из-за которой некоторые из наших инструкций psycopg2 терпят неудачу.Строка unicode Python, отклоненная psycopg

я свел проблему вплоть до ВПЧЭ:

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 для одиночного символа, вызывающего проблему, добавлена ​​информация, основанная на комментариях.

+0

Символ представляет [половину суррогатной пары] (Http: //www.fileformat. info/info/unicode/char/d837/index.htm) и не представляет собой собственную кодовую точку. Предположительно, вы получили его через API, который разделяет строку с кодировкой UTF-16, не обращая внимания на границы символов. – user4815162342

+0

@ user4815162342, так как я могу определить, содержит ли данная строка в python какие-либо такие неполные суррогатные пары? –

+0

Просто любопытно, мой ответ помог с вопросом? – user4815162342

ответ

2

Строка 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 
2

Чтобы обнаружить, что строка недействительна utf-8, просто заверните попытку закодировать ее внутри try/except перед выполнением ее в psycopg2.

Что касается проблемы, в середине строки есть определенный символ, который кодируется utf-16: \U000d8a85. Так что не Postgres не считает это utf-8, это действительно не так.

+1

Спасибо за объяснение, но 'x.encode ('utf-8')' не вызывает исключения. Также не используется 'x.encode ('utf-8'). Decode ('utf-8')'. Который побуждает меня поверить: python считает, что это действительный utf-8, или python имеет резервные копии для декодирования utf-8 нестрогим способом. –

+0

Кроме того, после дальнейшего возиться, кажется, что характер, вызывающий проблему, - '\ ud837' - любая идея, что там происходит? –

Смежные вопросы