Как настроить pyodbc для правильного приема строк из SQL Server с использованием freeTDS и unixODBC?

Я не могу получить действительную строку с сервера MSSQL в Python. Я считаю, что где-то существует несоответствие кодировки. Я полагаю, что это между уровнем ODBC и питоном, потому что я могу получить читаемые результаты в TSQL и ISQL.

Какую кодировку символов ожидает pyodbc? Что мне нужно изменить в цепочке, чтобы это заработало?

Конкретный пример

Вот упрощенный скрипт на Python в качестве примера:

#!/usr/bin/env python
import pyodbc

dsn = 'yourdb'
user = 'import'
password = 'get0lddata'
database = 'YourDb'

def get_cursor():
    con_string = 'DSN=%s;UID=%s;PWD=%s;DATABASE=%s;' % (dsn, user, password, database)
    conn = pyodbc.connect(con_string)
    return conn.cursor()

if __name__ == '__main__':
    c = get_cursor()
    c.execute("select id, name from recipe where id = 4140567")

    row = c.fetchone()
    if row:
        print row

Результат этого сценария:

(Decimal('4140567'), u'\U0072006f\U006e0061\U00650067')

В качестве альтернативы, если последняя строка скрипта изменяется на:

print "{0}, '{1}'".format(row.id, row.name)

Тогда результат:

Traceback (most recent call last):
  File "/home/mdenson/projects/test.py", line 20, in <module>
    print "{0}, '{1}'".format(row.id, row.name)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

Стенограмма, использующая tsql для выполнения того же запроса:

root@luke:~# tsql -S cmw -U import -P get0lddata
locale is "C"
locale charset is "ANSI_X3.4-1968"
using default charset "UTF-8"
1> select id, name from recipe where id = 4140567
2> go
id      name
4140567 orange2
(1 row affected)

а также в isql:

root@luke:~# isql -v yourdb import get0lddata
SQL>  select id, name from recipe where id = 4140567
+----------------------+--------------------------+
| id                   | name                     |
+----------------------+--------------------------+
| 4140567              | orange2                  |
+----------------------+--------------------------+
SQLRowCount returns 1
1 rows fetched

Так что я работал над этим утром и выглядел высоко и низко и не понял, что не так.

подробности

Вот детали версии:

  • Клиент Ubuntu 12.04
  • freetds v0.91
  • unixodbc 2.2.14
  • Python 2.7.3
  • pyodbc 2.1.7-1 (из пакета ubuntu) и 3.0.7-beta06 (скомпилировано из исходного кода)

  • Сервер XP с SQL Server Express 2008 R2

Вот содержимое нескольких файлов конфигурации на клиенте.

/etc/freetds/freetds.conf

[global]
    tds version = 8.0
    text size = 64512
[cmw]
    host = 192.168.90.104
    port = 1433
    tds version = 8.0
    client charset = UTF-8

/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 =
FileUsage = 1

файла /etc/odbc.ini

[yourdb]
Driver = FreeTDS
Description = ODBC connection via FreeTDS
Trace = No
Servername = cmw
Database = YourDB
Charset = UTF-8

3 ответа

Решение

Так что после продолжения работы я теперь получаю символы юникода в python. К сожалению, решение, на которое я наткнулся, примерно такое же удовлетворяющее, как и поцелуй твоего кузена.

Я решил проблему, установив пакеты python3 и python3-dev, а затем заново собрав pyodbc с помощью python3.

Теперь, когда я сделал это, мои скрипты теперь работают, хотя я все еще запускаю их с Python 2.7.

Так что я не знаю, что было исправлено, но теперь это работает, и я могу перейти к проекту, с которого я начал.

Есть ли шанс, что у вас возникли проблемы с спецификацией (Byte Order Marker)? Если это так, возможно, этот фрагмент кода поможет:

import codecs
if s.beginswith( codecs.BOM_UTF8 ):
    # The byte string s begins with the BOM: Do something.
    # For example, decode the string as UTF-8

if u[0] == unicode( codecs.BOM_UTF8, "utf8" ):
    # The unicode string begins with the BOM: Do something.
    # For example, remove the character.

# Strip the BOM from the beginning of the Unicode string, if it exists
u.lstrip( unicode( codecs.BOM_UTF8, "utf8" ) )

Я нашел этот фрагмент на этой странице.

Если вы обновите pyodbc до версии 3, проблема будет решена.

Другие вопросы по тегам