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