Назад | Перейти на главную страницу

Проблема с кодировкой Python

У меня есть сценарий python, который экспортирует данные из таблицы mysql utf-8 в текстовый файл. Вот код, который выполняет эту работу

csvDatei = codecs.open( csvDateiName, "w", "utf-8" )
...
cursor = db.cursor();
sql = "select * from %s.%s;" % (dbAusgang, tabelle)
cursor.execute(sql);
...
daten = cursor.fetchall();
for i in xrange(len(daten)):
    line = '';
    for j in xrange(len(daten[i])):
        line += '"%s";' % unicode(daten[i][j]);
    line = line[:-1];
    line += '\n';
    csvDatei.write(line);
csvDatei.close();

Я тоже пробовал это

line += '"%s";' % str(daten[i][j]);

и

line += '"%s";' % daten[i][j];

А теперь часть, которую я не понимаю:

Обычно этот сценарий должен запускаться заданием cron. Но когда я читаю varchar из таблицы, содержащей умляут вроде ä, ö или ü, сценарий просто завершается. Я проверил это, записав вывод скрипта в файл.

Поэтому я протестировал сценарий, вызвав его вручную в оболочке, просто набрав «python myscript.py», и он отлично работает без каких-либо проблем.

Поэтому я предполагаю, что проблема на самом деле не в самом скрипте, а как-то в среде cron.

Надеюсь, кто-нибудь из вас сможет дать мне совет. Я совершенно запутался.

Любая помощь приветствуется.

---------------- Ответ на комментарий 1:

Спасибо за подсказку с локалью.

Сначала я написал "локаль" на стандартной оболочке. Это дало мне следующий результат:

dhl@srv1093:~$ locale
LANG=de_DE.UTF-8
LC_CTYPE="de_DE.UTF-8"
LC_NUMERIC="de_DE.UTF-8"
LC_TIME="de_DE.UTF-8"
LC_COLLATE="de_DE.UTF-8"
LC_MONETARY="de_DE.UTF-8"
LC_MESSAGES="de_DE.UTF-8"
LC_PAPER="de_DE.UTF-8"
LC_NAME="de_DE.UTF-8"
LC_ADDRESS="de_DE.UTF-8"
LC_TELEPHONE="de_DE.UTF-8"
LC_MEASUREMENT="de_DE.UTF-8"
LC_IDENTIFICATION="de_DE.UTF-8"
LC_ALL=de_DE.UTF-8

Затем я отредактировал файл cron с помощью «crontab -e» и добавил следующую строку

*/1 * * * * locale > /home/user/locale.ouput

Результат этого cronjob:

dhl@srv1093:~$ cat locale.ouput 
LANG=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=

Итак, это может быть проблемой? Как я могу это исправить?

Вы декодируете строки в Unicode с помощью unicode(daten[i][j]). Когда вы не указываете кодировку, Python использует системное значение по умолчанию, которое, вероятно, является ascii при запуске скрипта через cron.

В любом случае вы должны указать фактическую кодировку, используемую базой данных. Ты можешь использовать unicode(daten[i][j], dbencoding) вместо этого или попросите адаптер базы данных напрямую выдавать вам Unicode.

Кстати: существует, вероятно, миллион инструментов, которые генерируют файлы cvs из запросов к базе данных, MySQL имеет это даже встроенное. Ваш код, с другой стороны, довольно хрупок, потому что вы вообще не можете убежать.

Хорошо, теперь я выяснил, в чем проблема. Это не имеет ничего общего с кодом, ну ладно, это уже было ясно раньше, но проблема в переменных локального языка.

В задании cron кодировки установлены на POSIX, а в обычном режиме SHELL кодировки установлены на UTF-8. Поэтому я изменил кодировки с UTF-8 на POSIX и запустил свой скрипт. И неожиданно возникает та же ошибка, что и в среде cron. Итак, теперь я меняю кодировку шаг за шагом, я имею в виду переменную за переменной и проверяю, запускается мой скрипт или нет.

Сначала я изменился

экспорт LANG = de_DE.UTF-8

и при запуске скрипта осталась та же ошибка. Затем после этого я изменился

экспорт LC_CTYPE = "de_DE.UTF-8"

и тогда сценарий работал абсолютно нормально. Без проблем.

Так вот в чем проблема. Как теперь изменить эту переменную в среде cron? Я уже пробовал в коде

locale.setlocale(locale.LC_CTYPE, 'de_DE.UTF-8')

Но это не сработало.

Как мне это изменить?

Я почти уверен, что проблема в этом. MySQL рассмотрит настройки вашей локали, чтобы определить кодировку символов для возврата значений. Я также знаю, что латинские символы с умляутами при кодировании в ISO-8859-1 не являются допустимыми символами UTF-8, и любой декодер завершится ошибкой, если попытается декодировать их (и без установки локали ваш клиентский модуль db может использовать это по умолчанию). Я не пробовал и не знаю, какую версию python вы используете, но гуглил python locale вернул эту ссылку: http://docs.python.org/library/locale.html Так. я бы попробовал

import locale
locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8')

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