Как разобрать несколько дат из блока текста в Python (или другом языке) - PullRequest
16 голосов
/ 11 августа 2011

У меня есть строка с несколькими значениями даты, и я хочу разобрать их все.Строка на естественном языке, поэтому лучшее, что я нашел на данный момент, это dateutil .

К сожалению, если в строке несколько значений даты, dateutil выдает ошибку:

>>> s = "I like peas on 2011-04-23, and I also like them on easter and my birthday, the 29th of July, 1928"
>>> parse(s, fuzzy=True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/pymodules/python2.7/dateutil/parser.py", line 697, in parse
    return DEFAULTPARSER.parse(timestr, **kwargs)
  File "/usr/lib/pymodules/python2.7/dateutil/parser.py", line 303, in parse
    raise ValueError, "unknown string format"
ValueError: unknown string format

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

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

PS - Полагаю, я мог бы рекурсивно разделить входной файл посередине и попробовать, попробуйте еще раз, пока он не заработает, но это чертовски хак.

Ответы [ 4 ]

16 голосов
/ 11 августа 2011

Глядя на это, наименее хакерским способом было бы изменить dateutil parser , чтобы иметь параметр нечеткого множественного числа.

parser._parse берет вашу строку, маркирует ее с помощью _timelexи затем сравнивает токены с данными, определенными в parserinfo.

Здесь , если токен ничего не соответствует в parserinfo, анализ не будет выполнен, если fuzzy не является True.

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

Не нужно слишком много усилий.


Обновление

Пока вы ждете, когда ваш патч будет добавлен ...

Это немного хакерский, использует непубличные функции в библиотеке, но не требует изменения библиотеки и не является методом проб и ошибок.У вас могут быть ложные срабатывания, если у вас есть какие-нибудь жетоны, которые можно превратить в плавающие.Возможно, вам придется отфильтровать результаты еще немного.

from dateutil.parser import _timelex, parser

a = "I like peas on 2011-04-23, and I also like them on easter and my birthday, the 29th of July, 1928"

p = parser()
info = p.info

def timetoken(token):
  try:
    float(token)
    return True
  except ValueError:
    pass
  return any(f(token) for f in (info.jump,info.weekday,info.month,info.hms,info.ampm,info.pertain,info.utczone,info.tzoffset))

def timesplit(input_string):
  batch = []
  for token in _timelex(input_string):
    if timetoken(token):
      if info.jump(token):
        continue
      batch.append(token)
    else:
      if batch:
        yield " ".join(batch)
        batch = []
  if batch:
    yield " ".join(batch)

for item in timesplit(a):
  print "Found:", item
  print "Parsed:", p.parse(item)

Выход:

Found: 2011 04 23
Parsed: 2011-04-23 00:00:00
Found: 29 July 1928
Parsed: 1928-07-29 00:00:00

Обновление для Дитера

Dateutil 2.1Похоже, что он написан для совместимости с python3 и использует библиотеку «совместимости» под названием six.Что-то не так с этим, и он не рассматривает str объекты как текст.

Это решение работает с dateutil 2.1, если вы передаете строки как Unicode или как объекты, похожие на файлы:

from cStringIO import StringIO
for item in timesplit(StringIO(a)):
  print "Found:", item
  print "Parsed:", p.parse(StringIO(item))

Если вы хотите установить опцию для parserinfo, создайте экземпляр parserinfo и передайте его объекту parser.Например:

from dateutil.parser import _timelex, parser, parserinfo
info = parserinfo(dayfirst=True)
p = parser(info)
5 голосов
/ 11 августа 2011

Пока я был в автономном режиме, меня беспокоил ответ, который я разместил здесь вчера.Да, он сделал свою работу, но это было излишне сложно и крайне неэффективно.

Вот издание с надписью «конверт», которое должно работать намного лучше!

import itertools
from dateutil import parser

jumpwords = set(parser.parserinfo.JUMP)
keywords = set(kw.lower() for kw in itertools.chain(
    parser.parserinfo.UTCZONE,
    parser.parserinfo.PERTAIN,
    (x for s in parser.parserinfo.WEEKDAYS for x in s),
    (x for s in parser.parserinfo.MONTHS for x in s),
    (x for s in parser.parserinfo.HMS for x in s),
    (x for s in parser.parserinfo.AMPM for x in s),
))

def parse_multiple(s):
    def is_valid_kw(s):
        try:  # is it a number?
            float(s)
            return True
        except ValueError:
            return s.lower() in keywords

    def _split(s):
        kw_found = False
        tokens = parser._timelex.split(s)
        for i in xrange(len(tokens)):
            if tokens[i] in jumpwords:
                continue 
            if not kw_found and is_valid_kw(tokens[i]):
                kw_found = True
                start = i
            elif kw_found and not is_valid_kw(tokens[i]):
                kw_found = False
                yield "".join(tokens[start:i])
        # handle date at end of input str
        if kw_found:
            yield "".join(tokens[start:])

    return [parser.parse(x) for x in _split(s)]

Пример использования:

>>> parse_multiple("I like peas on 2011-04-23, and I also like them on easter and my birthday, the 29th of July, 1928")
[datetime.datetime(2011, 4, 23, 0, 0), datetime.datetime(1928, 7, 29, 0, 0)]

Вероятно, стоит отметить, что его поведение немного отличается от dateutil.parser.parse при работе с пустыми / неизвестными строками.Dateutil вернет текущий день, в то время как parse_multiple возвращает пустой список, который, IMHO, это то, что и следовало ожидать.

>>> from dateutil import parser
>>> parser.parse("")
datetime.datetime(2011, 8, 12, 0, 0)
>>> parse_multiple("")
[]

PS Только что заметил Обновленный ответ MattH делает что-то очень похожее.

0 голосов
/ 11 августа 2011

Почему бы не написать шаблон регулярных выражений, охватывающий все возможные формы, в которых может появляться дата, а затем запустить регулярное выражение для изучения текста? Я предполагаю, что не существует дюжины десятков способов выразить дату в строке.

Единственная проблема - собрать максимальное количество выражений даты

0 голосов
/ 11 августа 2011

Я думаю, если вы поместите «слова» в массив, это должно сработать. С этим вы можете проверить, является ли это дата или нет, и положить в переменную.

Когда у вас есть дата, вы должны использовать datetime library library.

...