«Умный» (прощающий) парсер дат? - PullRequest
5 голосов
/ 09 июля 2009

Мне нужно перенести очень большой набор данных из одной системы в другую. Один из столбцов «источник» содержит дату, но на самом деле это строка без ограничений, в то время как система назначения назначает дату в формате гггг-мм-дд.

Многие, но не все исходные даты форматируются как ггггммдд. Чтобы привести их к ожидаемому формату, я делаю (в Perl):

return "$1-$2-$3" if ($val =~ /(\d{4})[-\/]*(\d{2})[-\/]*(\d{2})/);

Проблема возникает, когда исходные даты удаляются от «общего» ггггммдд. Цель состоит в том, чтобы спасти как можно больше дат, прежде чем сдаться. Примеры исходных строк:

21/3/1998, Март 2004 года, 2001, 3/4/97 & times; 1008 *

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

Но есть ли что-нибудь умнее, чтобы сделать? Разве я не изобретаю велосипед? Есть ли где-нибудь библиотека, которая делает что-то подобное? Я не смог найти ничего подходящего, прибегая к помощи "прощающего парсера дат". (любой язык в порядке).

Ответы [ 5 ]

4 голосов
/ 09 июля 2009

Date :: Manip - ваш друг, так как он терпит неудачу только на одном из четырех, потому что он принимает формат США, используя Date_Init, вы можете получить 4 из 4.

имеют разные форматы (например, месяц до дня и наоборот), вам придется анализировать их по-разному: один раз с форматом даты в США, а другой - с форматом даты, отличным от американского.Это особенно важно, когда это неоднозначно, как, например, ваш пример от 3/4/97, потому что, если он 21/3, он просто терпит неудачу, и вы можете сказать, что формат неправильный.

vinko@mithril:~$ more date.pl
use strict;
use warnings;
use Date::Manip;

my @a;
push @a, "March 2004";
push @a, "2001";
push @a, "3/4/97";
push @a, "21/3/1998";
Date_Init("DateFormat=non-US");
for my $d (@a) {
    print "$d\n";
    print ParseDate($d)."\n";
};
vinko@mithril:~$ perl date.pl
March 2004
2004030100:00:00
2001
2001010100:00:00
3/4/97
1997040300:00:00
21/3/1998
1998032100:00:00
4 голосов
/ 09 июля 2009

Вы ищете модуль Date :: Parse ?

2 голосов
/ 21 марта 2010

Я наконец извлек тестовый набор из более чем 200 примеров дат, которые фактически встречаются в наборе данных. Некоторые из них плохо себя ведут, некоторые больны (например, «01010»).

Я перепробовал все существующие модули Perl, которые смог найти, но вероятность успеха была слишком низкой. В конечном итоге я нырнул в свое новое колесо, достигнув более чем 98% успеха.

Мой алгоритм представляет собой последовательность все более и более нечетких распознавателей, начиная с строго достоверных дат, вплоть до общей территории предположений. Первый, вернувший результат «успеха», побеждает. В середине этого стека у меня есть «основной» распознаватель, который делает что-то вроде этого:

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

  • Для каждого из них я поместил их в три ведра: кандидаты на год, кандидаты на месяц, кандидаты на день. Например, «13» будет в корзине «возможного года» и в корзине «возможного дня». «Февраль» будет идти только в «месяце», конечно. В каждом сегменте значение помечается «уровнем правдоподобия», произвольным числом, которое зависит от ряда вещей. Например, 2010 год более правдоподобен как год, чем 10.

  • загляните в каждое из трех ведер. Если у любого из них есть только один элемент, это значение для этого сегмента. Это также удалено из других ведер.

  • ищите оставшиеся пропущенные значения в соответствующих сегментах по порядку (год, месяц, день), выбирая значение с наибольшей вероятностью. В случае связи, возьмите тот, который встречается последним в строке (на самом деле, они имеют немного большую правдоподобность). Это правило нарушает 7/3/2010 как 7 марта, как мне нужно здесь, во Франции. Удалите это значение из других групп, если это применимо.

  • если какое-либо значение отсутствует, используйте значение по умолчанию (например, я использую 8191 как год по умолчанию, самое большое допустимое значение в моей целевой системе).

Все это ужасно эвристика, но соответствует моему требованию, что лучше иметь мусор, чем потерять информацию.

1 голос
/ 09 июля 2009

Вы также можете взглянуть на DateTime :: Format :: Flexible

Исходя из его описания, он прямо по вашей аллее:

Если вам когда-либо приходилось использовать программу что заставило вас ввести дату определенным образом и подумал "Почему не может компьютер просто выяснить, какую дату я хотел? ", этот модуль для вас.

DateTime :: Format :: Гибкие попытки возьмите любую строку, которую вы даете, и анализируйте это в объект DateTime.

Я запустил версию скрипта Винко, используя этот модуль, и получил похожие результаты. Все хорошо, за исключением последнего случая (21/3/1998). Как и в случае Date::Manip, вы можете относительно легко справиться с этим, явно указав параметр (european => 1). Комментарий Дэнбистрома показывает, почему такие случаи требуют человеческого контроля.

0 голосов
/ 09 июля 2009

Это не Perl, но эта библиотека .NET будет анализировать широкий диапазон строк даты / времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...