使用 pyodbc 和 SQL 的 nvarchar 字段出现此 UnicodeDecodeError 的原因是什么?
What's the cause of this UnicodeDecodeError with an nvarchar field using pyodbc and MSSQL?
我可以通过 pypyodbc 在 python 中发送查询来读取 MSSQL 数据库。
大多数 unicode 字符都被正确处理,但我遇到了导致错误的特定字符。
有问题的字段是 nvarchar(50)
类型并以这个字符“”开头,这对我来说有点像这样...
-----
|100|
|111|
-----
如果该数字是十六进制 0x100111
则它是字符 supplementary private use area-b u+100111
。不过有趣的是,如果是二进制的0b100111
那么就是撇号,会不会是上传数据时使用了错误的编码?此字段存储部分中文邮政地址。
错误信息包括
UnicodeDecodeError: 'utf16' codec can't decode bytes in position 0-1: unexpected end of data
这是完整的...
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 搜索并检查了该网站的相关问题,但没有成功。
这个问题最终得到了解决,我怀疑问题是当 table 被设置时,文本有一个编码的字符通过一些 hacky 方法被锤击到另一个声明编码的字段中。
我自己用这个解决了这个问题:
conn.setencoding('utf-8')
在创建游标之前。
其中 conn
是连接对象。
我正在使用 fetchall()
获取数千万行,并且在手动撤消非常昂贵的事务中间,所以我不能简单地跳过无效的行。
我找到解决方案的来源:https://github.com/mkleehammer/pyodbc/issues/112#issuecomment-264734456
我可以通过 pypyodbc 在 python 中发送查询来读取 MSSQL 数据库。
大多数 unicode 字符都被正确处理,但我遇到了导致错误的特定字符。
有问题的字段是 nvarchar(50)
类型并以这个字符“”开头,这对我来说有点像这样...
-----
|100|
|111|
-----
如果该数字是十六进制 0x100111
则它是字符 supplementary private use area-b u+100111
。不过有趣的是,如果是二进制的0b100111
那么就是撇号,会不会是上传数据时使用了错误的编码?此字段存储部分中文邮政地址。
错误信息包括
UnicodeDecodeError: 'utf16' codec can't decode bytes in position 0-1: unexpected end of data
这是完整的...
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 搜索并检查了该网站的相关问题,但没有成功。
这个问题最终得到了解决,我怀疑问题是当 table 被设置时,文本有一个编码的字符通过一些 hacky 方法被锤击到另一个声明编码的字段中。
我自己用这个解决了这个问题:
conn.setencoding('utf-8')
在创建游标之前。
其中 conn
是连接对象。
我正在使用 fetchall()
获取数千万行,并且在手动撤消非常昂贵的事务中间,所以我不能简单地跳过无效的行。
我找到解决方案的来源:https://github.com/mkleehammer/pyodbc/issues/112#issuecomment-264734456