如何使用python_dateutil 1.5的'parse'函数处理unicode?

9 投票
2 回答
2390 浏览
提问于 2025-04-17 10:26

我需要让 Python_dateutil 1.5 的 parse() 函数能够处理 Unicode 格式的月份名称。

如果我使用 fuzzy=True,它会跳过月份名称,结果就会把月份设为 1。

当我不使用 fuzzy 参数时,我遇到了以下错误:

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 个回答

3

我查看了 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)
8

Rik Poggi说得对,字符串'Июнь'在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会把日期字符串中的每个字节当作一个单独的字符来处理。这就是Rik观察到的情况。

另一个大问题是dateutil在内部处理时使用的是Python的字节字符串,而不是Unicode字符串。这意味着,即使_timelex被扩展以接受西里尔字母,字节和字符之间的处理仍然会不匹配,而且由于调用者和python_dateutil源代码之间的字符串编码差异,也会出现问题。

还有一些小问题,比如假设每个月的名称至少有3个字符(这对日语来说不成立),以及与公历相关的许多细节。如果wordchars字段能够从parserinfo中获取(如果存在的话),那么parserinfo就可以为其月份和日期名称定义正确的字符集,这样会更好。

python_dateutil的版本2.0已经移植到Python 3,但上述设计问题并没有显著改变。2.0和1.5之间的区别主要是为了处理Python语言的变化,而不是dateutil的设计和数据结构。

Oleg,你能够修改parserinfo,我怀疑你成功的原因是你的测试代码没有使用python_dateutilparser()(和_timelex)。实际上,你提供了自己的解析器和词法分析器。

要解决这个问题,需要对python_dateutil的文本处理进行相当大的改进。如果有人能做出这样的补丁,并且包的维护者能够将其合并,那就太好了。

撰写回答