使用pyodbc和MSSQL的nvarchar字段导致此UnicodeDecodeError的原因是什么?

时间:2016-04-18 12:18:13

标签: python sql-server unicode pyodbc pypyodbc

我可以通过pypyodbc在python中发送查询来从MSSQL数据库中读取。

大多数unicode字符都是正确处理的,但是我遇到了导致错误的特定字符。

相关字段的类型为nvarchar(50),并以此字符开头""这让我觉得有点像......

-----
|100|
|111| 
-----

如果该数字为十六进制0x100111,那么它就是字符supplementary private use area-b u+100111。虽然有趣的是,如果它是二进制文件0b100111,那么它是一个撇号,是否可能是在上传数据时使用了错误的编码?该字段存储了中文邮政地址的一部分。

错误消息包括

  

UnicodeDecodeError:' utf16'编解码器不能解码位置0-1中的字节:意外的数据结束

这里完全......

Traceback (most recent call last):   File "question.py", line 19, in <module>
    results.fetchone()   File "/VIRTUAL_ENVIRONMENT_DIR/local/lib/python2.7/site-packages/pypyodbc.py", line 1869, in fetchone
    value_list.append(buf_cvt_func(from_buffer_u(alloc_buffer)))   File "/VIRTUAL_ENVIRONMENT_DIR/local/lib/python2.7/site-packages/pypyodbc.py", line 482, in UCS_dec
    uchar = buffer.raw[i:i + ucs_length].decode(odbc_decoding)   File "/VIRTUAL_ENVIRONMENT_DIR/lib/python2.7/encodings/utf_16.py", line 16, in decode
    return codecs.utf_16_decode(input, errors, True) UnicodeDecodeError: 'utf16' codec can't decode bytes in position 0-1: unexpected end of data

这里有一些最小的再现代码......

import pypyodbc

connection_string = (
    "DSN=sqlserverdatasource;"
    "UID=REDACTED;"
    "PWD=REDACTED;"
    "DATABASE=obi_load")

connection = pypyodbc.connect(connection_string)

cursor = connection.cursor()

query_sql = (
    "SELECT address_line_1 "
    "FROM address "
    "WHERE address_id == 'REDACTED' ")

with cursor.execute(query_sql) as results:
    row = results.fetchone() # This is the line that raises the error.
    print row

这是我/etc/freetds/freetds.conf

的一大块
[global]
;   tds version = 4.2
;   dump file = /tmp/freetds.log
;   debug flags = 0xffff
;   timeout = 10
;   connect timeout = 10
    text size = 64512

[sqlserver]
host = REDACTED
port = 1433
tds version = 7.0
client charset = UTF-8

我也尝试过使用client charset = UTF-16并一起省略该行。

这是我/etc/odbc.ini

中的相关数据块
[sqlserverdatasource]
Driver = FreeTDS
Description = ODBC connection via FreeTDS
Trace = No
Servername = sqlserver
Database = REDACTED

这是我/etc/odbcinst.ini

中的相关数据块
[FreeTDS]
Description = TDS Driver (Sybase/MS SQL)
Driver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so
Setup = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so
CPTimeout =
CPReuse =
UsageCount = 1

我可以通过在try / except块中获取结果来解决此问题,抛弃任何引发UnicodeDecodeError的行,但是有解决方案吗?我可以丢弃不可解码的字符,或者有没有办法获取此行而不会引发错误?

一些不良数据已经在数据库中结束,这是不可想象的。

我已经用Google搜索并检查了这个网站的相关问题,但没有运气。

2 个答案:

答案 0 :(得分:0)

这个问题最终解决了,我怀疑问题是文本在设置表时,通过一些hacky方法将一个编码的字符敲入一个字段,并使用另一个声明的编码。

答案 1 :(得分:0)

我自己解决了这个问题:

conn.setencoding('utf-8')

在创建游标之前。

conn是连接对象。

我使用fetchall()获取了数千万行,并且在交易过程中手动撤消会非常昂贵,因此我无法轻易跳过无效行。

我找到解决方案的来源:https://github.com/mkleehammer/pyodbc/issues/112#issuecomment-264734456