UTF-8 В протоколе Python, как? - программирование
Подтвердить что ты не робот

UTF-8 В протоколе Python, как?

Я пытаюсь записать кодированную строку UTF-8 в файл, используя пакет протоколирования Python. В качестве примера игрушки:

import logging

def logging_test():
    handler = logging.FileHandler("/home/ted/logfile.txt", "w",
                                  encoding = "UTF-8")
    formatter = logging.Formatter("%(message)s")
    handler.setFormatter(formatter)
    root_logger = logging.getLogger()
    root_logger.addHandler(handler)
    root_logger.setLevel(logging.INFO)

    # This is an o with a hat on it.
    byte_string = '\xc3\xb4'
    unicode_string = unicode("\xc3\xb4", "utf-8")

    print "printed unicode object: %s" % unicode_string

    # Explode
    root_logger.info(unicode_string)

if __name__ == "__main__":
    logging_test()

Это взрывается с помощью UnicodeDecodeError при вызове logging.info().

На более низком уровне пакет протоколов Python использует пакет кодеков для открытия файла журнала, передавая в качестве аргумента аргумент "UTF-8". Это хорошо и хорошо, но он пытается записать строки байтов в файл вместо объектов unicode, которые взрываются. По сути, Python делает это:

file_handler.write(unicode_string.encode("UTF-8"))

Когда это должно быть сделано:

file_handler.write(unicode_string)

Является ли это ошибкой в ​​Python, или я беру сумасшедшие таблетки? FWIW, это запасная установка Python 2.6.

4b9b3361

Ответ 1

Убедитесь, что у вас есть последний Python 2.6 - некоторые ошибки Unicode были найдены и исправлены с тех пор, как вышел 2.6. Например, в моей системе Ubuntu Jaunty я скопировал и вставил ваш script, удалив только префикс '/home/ted/' из имени файла журнала. Результат (скопирован и вставлен из окна терминала):

[email protected]:~/projects/scratch$ python --version
Python 2.6.2
[email protected]:~/projects/scratch$ python utest.py 
printed unicode object: ô
[email protected]:~/projects/scratch$ cat logfile.txt 
ô
[email protected]:~/projects/scratch$ 

В окне Windows:

C:\temp>python --version
Python 2.6.2

C:\temp>python utest.py
printed unicode object: ô

И содержимое файла:

alt text

Это также может объяснить, почему Леннарт Регебро не смог воспроизвести его.

Ответ 2

Имея код вроде:

raise Exception(u'щ')

Вызванный:

  File "/usr/lib/python2.7/logging/__init__.py", line 467, in format
    s = self._fmt % record.__dict__
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)

Это происходит потому, что строка формата является байтовой строкой, в то время как некоторые аргументы строки форматирования являются строками unicode с символами, отличными от ASCII:

>>> "%(message)s" % {'message': Exception(u'\u0449')}
*** UnicodeEncodeError: 'ascii' codec can't encode character u'\u0449' in position 0: ordinal not in range(128)

Создание строки формата unicode устраняет проблему:

>>> u"%(message)s" % {'message': Exception(u'\u0449')}
u'\u0449'

Итак, в вашей конфигурации ведения журнала сделайте всю строку формата unicode:

'formatters': {
    'simple': {
        'format': u'%(asctime)-s %(levelname)s [%(name)s]: %(message)s',
        'datefmt': '%Y-%m-%d %H:%M:%S',
    },
 ...

И исправьте форматтера logging по умолчанию, чтобы использовать строку формата unicode:

logging._defaultFormatter = logging.Formatter(u"%(message)s")

Ответ 3

Попробуйте следующее:

import logging

def logging_test():
    log = open("./logfile.txt", "w")
    handler = logging.StreamHandler(log)
    formatter = logging.Formatter("%(message)s")
    handler.setFormatter(formatter)
    root_logger = logging.getLogger()
    root_logger.addHandler(handler)
    root_logger.setLevel(logging.INFO)

    # This is an o with a hat on it.
    byte_string = '\xc3\xb4'
    unicode_string = unicode("\xc3\xb4", "utf-8")

    print "printed unicode object: %s" % unicode_string

    # Explode
    root_logger.info(unicode_string.encode("utf8", "replace"))


if __name__ == "__main__":
    logging_test()

Для чего я должен был использовать codecs.open, чтобы открыть файл с помощью кодировки utf-8, но либо по умолчанию, либо что-то еще происходит здесь, так как он работает так, как будто.

Ответ 4

Если я правильно понял вашу проблему, такая же проблема возникнет в вашей системе, когда вы это сделаете:

str(u'ô')

Я предполагаю, что автоматическая кодировка кодировки локали в Unix не будет работать до тех пор, пока вы не включите ветвь if, поддерживающую языковой стандарт в setencoding, в site через locale. Обычно этот файл находится в /usr/lib/python2.x, он все равно проверяет. AFAIK, locale-aware setencoding отключен по умолчанию (это верно для моей установки Python 2.6).

Возможные варианты:

  • Пусть система определит правильный способ кодирования строк Unicode в байтах или сделать это в вашем коде (требуется некоторая конфигурация в специфичном для сайта site.py).
  • Кодировать строки Unicode в вашем коде и выводить только байты

См. также Иллюзионное кодирование setdefault с помощью Ian Bicking и связанных ссылок.

Ответ 5

У меня была похожая проблема с запуском Django в Python3: мой регистратор умер при столкновении с некоторыми умлаутами (äöüß), но в остальном все было в порядке. Я просмотрел много результатов и не нашел ничего работающего. Я старался

import locale; 
if locale.getpreferredencoding().upper() != 'UTF-8': 
    locale.setlocale(locale.LC_ALL, 'en_US.UTF-8') 

который я получил из комментария выше. Это не работает. Глядя на текущую локализацию, я получил сумасшедшую вещь ANSI, которая в основном означала просто "ASCII". Это отправило меня в совершенно неверное русло.

Изменение формата строки журнала на Unicode не поможет. Установка магического комментария кодировки в начале журнала не поможет. Установка кодировки в сообщении отправителя (текст пришел из HTTP-запроса) не помогла.

Что DID работало, так это установило кодировку для обработчика файлов в UTF-8 в settings.py. Поскольку у меня ничего не было установлено, по умолчанию будет None. Который, очевидно, заканчивается ASCII (или, как мне хотелось бы думать: ASS-KEY)

    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.handlers.TimedRotatingFileHandler',
            'encoding': 'UTF-8', # <-- That was missing.
            ....
        },
    },