Как использовать функцию 'parse' python_dateutil 1.5 для работы с юникодом? - PullRequest
9 голосов
/ 17 января 2012

Мне нужно, чтобы Python_dateutil 1.5 parse () работал с названиями месяцев в Юникоде.

Если использовать fuzzy = True, оно пропускает название месяца и дает результат с месяцем = 1

Когда я использую его без нечеткого параметра, я получаю следующее исключение:

from dateutil.parser import parserinfo, parser, parse

class myparserinfo(parserinfo):
    MONTHS = parserinfo.MONTHS[:]
    MONTHS[3] = (u"Foo", u"Foo", u"Июнь")


>>> test = unicode('8th of Июнь', 'utf-8')
>>> tester = parse(test, parserinfo=myparserinfo())
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\Python27\lib\site-packages\python_dateutil-1.5-py2.7.egg\dateutil\parser.py", line 695, in parse
    return parser(parserinfo).parse(timestr, **kwargs)
  File "C:\Python27\lib\site-packages\python_dateutil-1.5-py2.7.egg\dateutil\parser.py", line 303, in parse
    raise ValueError, "unknown string format"
ValueError: unknown string format

Ответы [ 2 ]

8 голосов
/ 19 января 2012

Рик Поджи прав, строка «Июнь» не может быть месяцем для python-dateutil. Немного углубившись в dateutil/parser.py, основная проблема в том, что этот модуль достаточно интернационализирован для работы с западноевропейскими языками латинского алфавита. Он не предназначен для работы с такими языками, как русский, с использованием нелатинских шрифтов, таких как кириллица.

Самым большим препятствием является dateutil/parser.py:45-48, где лексический анализатор class _timelex определяет символы, которые можно использовать в токенах, включая названия месяца и дня:

class _timelex(object):
    def __init__(self, instream):
        # ... [some material omitted] ...
        self.wordchars = ('abcdfeghijklmnopqrstuvwxyz'
                          'ABCDEFGHIJKLMNOPQRSTUVWXYZ_'
                          'ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ'
                          'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ')
        self.numchars = '0123456789'
        self.whitespace = ' \t\r\n'

Поскольку wordchars не включает буквы кириллицы, _timelex испускает каждый байт в строке даты как отдельный символ. Это то, что заметил Рик.

Другое большое препятствие заключается в том, что dateutil использует байтовые строки Python вместо строк Юникода для внутренней обработки. Это означает, что, даже если _timelex был расширен для приема букв кириллицы, все равно будут несоответствия между обработкой байтов и символов и проблемами, вызванными различием в кодировании строк между вызывающим и исходным кодом python_dateutil.

Существуют и другие незначительные проблемы, такие как предположение, что название каждого месяца имеет длину не менее 3 символов (не относится к японскому языку), а также множество деталей, связанных с григорианским календарем. Было бы полезно, чтобы поле wordchars было выбрано из parserinfo, если оно есть, чтобы parserinfo могла определить правильный набор символов для своих названий месяцев и дней.

python_dateutil v 2.0 был портирован на Python 3, но вышеперечисленные проблемы проектирования существенно не изменились. Различия между 2.0 и 1.5 связаны с изменениями языка Pyhon, а не с дизайном dateutil и структурами данных.

Олег, вы смогли изменить parserinfo, и я подозреваю, что вам это удалось, потому что ваш тестовый код не использовал parser()_timelex) python_dateutil. Вы по сути поставили свой собственный парсер и лексер.

Исправление этой проблемы потребовало бы довольно значительных улучшений в обработке текста python_dateutil. Было бы замечательно, если бы кто-то сделал патч с этим изменением, и сопровождающие пакета смогли бы включить его.

3 голосов
/ 17 января 2012

Я взглянул на исходный код в dateutil/parser.py и обнаружил, что строка 'Июнь' не может быть месяцем для dateutil .

Проблеманачинается, когда ваш timestr разделен.

В строке 349 у вас есть:

l = _timelex.split(timestr)

, и поскольку _timelex.split определен как:

def split(cls, s):      # at line 142
    return list(cls(s))

, вы получитеl будет:

['8', 'th', ' ', 'of', ' ', '\x18', '\x04', 'N', '\x04', '=', '\x04', 'L', '\x04']

вместо (более или менее) ожидаемого значения:

[u'8th', u'of', u'\u0418\u044e\u043d\u044c']

По этой причине проверка месяца возвращает None,что приводит к возникновению исключения.

# Check month name
value = info.month(l[i])

Возможное решение:

Переведите все на английский, а затем, если необходимо, обратно на русский.

Пример:

dictionary = {u"Июнь": 'June', u'ноябрь': 'November'}

for russian,english in dictionary.items():
    test = test.replace(russian,english)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...