Распознать произвольную строку даты - PullRequest
22 голосов
/ 03 октября 2010

Мне нужно иметь возможность распознавать строки даты.Не имеет значения, если я не могу различить месяц и дату (например, 12/12/10), мне просто нужно классифицировать строку как дату, а не преобразовывать ее в объект Date.Таким образом, это действительно проблема классификации, а не анализа.

У меня будут такие фрагменты текста, как:

"бла бла бла бла * 12 января 09 бла бла бла бла 01/04/10 бла бла бла "

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

Мне было интересно, знает ли кто-нибудь какие-либо библиотеки Javaэто может сделать это.Мой гугл-фу пока ничего не придумал.

ОБНОВЛЕНИЕ: я должен быть в состоянии распознать самый широкий из возможных способов представления даты.Конечно, наивным решением может быть написание оператора if для каждого мыслимого формата, но в идеале подход распознавания образов *1018* с обученной моделью - в идеале то, что мне нужно.

Ответы [ 14 ]

5 голосов
/ 11 ноября 2010

Правила, которые могут помочь вам в вашем квесте:

  1. Создать или найти какую-то базу данных с известными словами, которые соответствуют месяцам. Сокращенные и полные имена, например Jan или January. При поиске он должен быть нечувствительным к регистру, потому что fEBruaRy также является месяцем, хотя человек, который его печатал, должен был быть пьян. Если вы планируете искать не английские месяцы, база данных также необходима, потому что никакая эвристика не обнаружит, что "Wrzesień" польский для сентября.
  2. Только для английского языка, проверьте порядковые номера , а также создайте базу данных для номеров от 1 до 31. Они будут полезны для дней и месяцев. Если вы хотите использовать этот подход для других языков, вам придется провести собственное исследование.
  3. Еще раз, только на английском языке, проверьте «Anno Domini» и «Перед Христом», то есть AD и BC соответственно. Они также могут быть в форме A.D. и B.C.
  4. Что касается самих чисел, которые будут представлять дни, месяцы и годы, вы должны знать, где находится ваш лимит. Это 0-9999 или больше? То есть, вы хотите найти даты, которые представляют годы после 9999 года? Если нет, то строки с 1-4 последовательными цифрами являются хорошими предположениями для действительного дня, месяца или года.
  5. Дни и месяцы состоят из одной или двух цифр. Допускаются начальные нули, поэтому допустимы строки с форматом 0*, где * может быть от 1 до 9.
  6. Разделители могут быть хитрыми, но если вы не допустите непоследовательное форматирование, например, 10/20 \ 1999, то вы сэкономите много горя. Это потому, что 10 * 20 * 1999 может быть допустимой датой, где * обычно является одним элементом набора {-,_, ,:,/,\,.,','}, но возможно, что * является комбинацией из 2 или 3 элементов упомянутого набора. Еще раз, вы должны выбрать приемлемые разделители. 10–20–1999 может быть подходящей датой для человека со странным чувством элегантности. 10/20/1999 также может быть допустимой датой, но 10 / 20_ / 1999 будет очень странной датой.
  7. В некоторых случаях нет разделителя. Например: 10 января 1988 года. В этих случаях используются слова из 1.
  8. Существуют особые случаи, такие как 28 или 29 февраля, в зависимости от високосного года. Также месяцы с 30 или 31 днем.

Я думаю, что этого достаточно для "наивной" классификации, эксперт-лингвист может вам помочь.

Теперь идея вашего алгоритма. Скорость не имеет значения. Там может быть несколько проходов по одной и той же строке. Оптимизируйте, когда это начнет иметь значение. Если вы сомневаетесь в том, что нашли строку даты, сохраните ее где-нибудь в «безопасном» месте в ListOfPossibleDates и проведите проверку еще раз, с более жесткими правилами, использующими комбинации от 1 до 8. Если вы считаете, что строка даты действительна, скормите его классу Date, чтобы увидеть, действительно ли он действителен. 32 марта 1999 года недействительно, если вы преобразуете его в формат, понятный Date.

Один важный повторяющийся паттерн - взгляд назад и вокруг. Когда вы считаете, что найдена действительная сущность (день, месяц, год), вам нужно будет увидеть, что скрывается за и после. Здесь может помочь механизм на основе стека или рекурсия.

Шаги:

  1. Поиск в строке слов из правила 1. Если вы найдете какое-либо из них, запомните это место. Обратите внимание на месяц. Теперь, пройдите несколько персонажей позади и несколько впереди, чтобы увидеть, что вас ждет. Если до и после вашего месяца нет пробелов и есть числа, как в правиле 7., проверьте их на достоверность. Если один из них представляет день (должен быть 0-31), а другой год (должен быть 0-9999, возможно с AD или BC), у вас есть один кандидат. Если есть одинаковые разделители до и после, ищите правила из 6. Всегда помните, что вы должны быть уверены, что существует допустимая комбинация. так, 32 января 1999 года не пойдет.
  2. Найдите в своей строке другие английские слова из правил 2. и 3. Повторите процедуру аналогично шагу 1.
  3. Поиск разделителей.Пустое пространство будет самым хитрым.Попробуйте найти их в парах.Итак, если у вас есть один «/» в вашей строке, найдите другой и посмотрите, что у них есть между ними.Если вы найдете комбинацию разделителей, то же самое.Также используйте алгоритм из шага 2.
  4. Поиск цифр.Допустимые значения: 0-9999, допускаются начальные нули.Если вы найдете один, ищите разделители, как в шаге 3.

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

Давайте рассмотрим ваш пример, "bla bla bla bla 12 Jan 09 bla bla bla 01/04/10 bla bla bla".После того, как вы извлечете первую дату, 12 Jan 09, затем используйте оставшуюся часть этой строки ("bla bla bla 01/04/10 bla bla bla") и повторите все вышеописанные шаги.Таким образом, вы будете уверены, что ничего не пропустили.

Я надеюсь, что эти предложения будут хоть как-то полезны.Если не существует библиотеки для выполнения всех этих грязных (и более) шагов за вас, тогда у вас впереди трудный путь.Удачи!

5 голосов
/ 08 ноября 2010

Использование JChronic

Вы можете использовать DateParser2 из пакета edu.mit.broad.genome.utils.

5 голосов
/ 19 октября 2010

Вы можете зациклить все доступные форматы даты в Java:

for (Locale locale : DateFormat.getAvailableLocales()) {
    for (int style =  DateFormat.FULL; style <= DateFormat.SHORT; style ++) {
        DateFormat df = DateFormat.getDateInstance(style, locale);
        try {
                df.parse(dateString);
                // either return "true", or return the Date obtained Date object
        } catch (ParseException ex) {
            continue; // unperasable, try the next one
        }
    }
}

Это, однако, не учитывает никаких пользовательских форматов даты.

4 голосов
/ 17 июля 2013

Очень хороший синтаксический анализатор даты в Java: Natty , вы можете попробовать его здесь

4 голосов
/ 07 ноября 2010

Я сделал это с огромным регулярным выражением (самостоятельно созданным):

public static final String DATE_REGEX = "\b([0-9]{1,2} ?([\\-/\\\\] ?[0-9]{1,2} ?| (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ?)([\\-/\\\\]? ?('?[0-9]{2}|[0-9]{4}))?)\b";
public static final Pattern DATE_PATTERN = Pattern.compile(DATE_REGEX, Pattern.CASE_INSENSITIVE); // Case insensitive is to match also "mar" and not only "Mar" for March

public static boolean containsDate(String str)
{
    Matcher matcher = pattern.matcher(str);
    return matcher.matches();
}

Это соответствует следующим датам:

06 Sep 2010
12-5-2005
07 Mar 95
30 DEC '99
11\9\2001

И не это:

444/11/11
bla11/11/11
11/11/11blah

Он также сопоставляет даты между символами, такими как [], (), ,:

Yesterday (6 nov 2010)

Соответствует датам без года:

Yesterday, 6 nov, was a rainy day...

Но это соответствует:

86-44/1234
00-00-0000
11\11/11

И это уже не похоже на свидание.Но это то, что вы можете решить, проверив, являются ли числа возможными значениями для месяца, дня, года.

2 голосов
/ 02 декабря 2013

Вот простой пример:

import com.joestelmach.natty.*;

List<Date> dates =new Parser().parse("Start date 11/30/2013 , end date Friday, Sept. 7, 2013").get(0).getDates();
        System.out.println(dates.get(0));
        System.out.println(dates.get(1));

//output:
        //Sat Nov 30 11:14:30 BDT 2013
        //Sat Sep 07 11:14:30 BDT 2013
2 голосов
/ 17 октября 2010

Я уверен, что исследователи в извлечении информации рассмотрели эту проблему, но я не смог найти статью.

Одна вещь, которую вы можете попробовать, это сделать в два этапа.(1) собрав как можно больше данных, извлеките элементы, некоторые функции, которые приходят на ум: количество чисел, которые появляются в строке, количество чисел от 1 до 31, которые появляются в строке, количество чисел от 1 до12, которые появляются в строке, количество месяцев, которые появляются в строке, и так далее.(2) изучите особенности, используя некоторый тип метода двоичной классификации (например, SVM) и, наконец, (3) когда появится новая строка, извлеките особенности и запросите SVM для прогноза.

1 голос
/ 10 ноября 2010

Что я хотел бы сделать, так это искать характеристики даты, а не сами даты.Например, вы можете искать косые черты (чтобы получить даты в форме 1/1/1001), тире (1 - 1 - 1001), названия месяцев и сокращения (1 января 1001 или 1 января 1001).Когда вы получите удар по ним, соберите близлежащие слова (по 2 с каждой стороны должно быть хорошо) и сохраните их в виде массива строк.После того, как вы отсканировали все входные данные, проверьте этот строковый массив с помощью функции, которая углубится в немного глубину и извлечет фактические строки даты, используя методы, найденные здесь.Важно просто сводить общие даты к управляемому уровню.

1 голос
/ 04 октября 2010

Практически невозможно распознать все возможные форматы даты как даты, используя «стандартные» алгоритмы. Это потому, что их так много.

Мы, люди, способны сделать это только потому, что узнали, что что-то вроде 2010-03-31 напоминает дату. Другими словами, я бы предложил использовать алгоритмы машинного обучения и научить вашу программу распознавать действительные последовательности дат. С Google Prediction API это должно быть возможным.

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

1 голос
/ 03 октября 2010

Может быть, вы должны использовать регулярные выражения?

Надеюсь, этот будет работать для формата mm-dd-yyyy:

^(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.](19|20)\d\d$

Здесь (0[1-9]|1[012]) соответствует месяцу 00..12, (0[1-9]|[12][0-9]|3[01]) соответствует дате 00..31 и (19|20)\d\d соответствует году.

Поля могут быть разделены тире, косой чертой или точкой.

С уважением, Serge

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