Pull to refresh

Comments 77

Классно получилось, спасибо! Форкнул, на досуге попробую сделать такое на PHP :)
Делал подобный парсер на php для своей домашнего xmpp-бота. Бот умел напоминать о событиях, делать записи в гугл-календарь и прочие приятные мелочи.
Так вот для php мне показалось удобнее использовать готовую функцию strtotime.
Чтобы обучить strtotime парсить даты на русском языке, просто переводил ключевые слова с русского на английский.
Вот так:
$keywords = array(
	"понедельник" => "monday", 
	"воскресенье" => "sunday", 
	"послезавтра" => "+2 days",
	"месяцев" => "months",
	"вторник" => "tuesday", 
...
	$result = strtotime(
			str_replace(array_keys($keywords), 
					array_values($keywords), 
					self::strtolower($time) )
					);


Работает неплохо.
Раньше тоже так делал, но потом весь мой мозг переехал на node.js
хорошая мысль! как раз для этого и хотел сделать парсер)))
$result = strtotime(strtr(self::strtolower($time), $keywords));
Мой код на PHP в 448 строк, генерирующий числительное в любом падеже: 123 => «сто двадцать три». Не пинайте особо, коду больше лет, чем коммиту. Передаю этот код в общественное достояние. Может быть, кому-то пригодится.
«Проснуться 2 января» выдает 2 января 2013 года.
Еще неплохо было бы, если бы распознавало что-то типа «7 вечера».
«Проснуться 2 января 2014» — можно так,
а можно зайти в код и подправить определение года, зная текущую дату. Добавил себе в планы.
UFO just landed and posted this here
У вас баг в распозновании «двадцать» и т.д., а «двадцать пять», я так понял вообще не реализовано.
Баг фиксится прсто — replace двадцати надо поставить раньше replace'а двух.
«двадцать пять» не реализовывал. А ваше замечание в примере исправил. Спасибо.
«в 21часов 30 минут» — о_О
А если написать «Сегодня в 21час 30 минут», то получим «Распознано как: + сегодня [21 30 ми:00]»
Ну, и сам код причиняет боль, да…
«Третий четверг каждого нечётного месяца — санитарный день»
Мы, кстати, спорили с женой, если сегодня понедельник, то что означает фраза: «следующий вторник»: завтрашний вторник, или через 8 дней?
Не могу не вспомнить людей, говорящих фразу «на той неделе». У меня она ассоциируется с прошлой неделей, а у энного количества людей старшего поколения по каким-то неведомым мне причинам со следующей.
Потому что «та» — противоположность «этой». «На той неделе» = «На другой неделе».
А «другая» — это какая неделя, прошлая или будущая?
UFO just landed and posted this here
Проблема не в неопределенности — тут, вы правы, контекст решает. Проблема исключительно холиварного характера — вроде бы и правильно, но все равно коробит, когда будущую неделю называют «той».
вроде бы и правильно, но все равно коробит, когда будущую неделю называют «той».
И не одного вас коробит!
— А у Васи день рождения уже прошел или будет только?
— На той неделе.
Думаю, тут как с «остановите на следующей остановке» — чем ближе к остановке это сказано, тем менее вероятно что имеется ввиду ближайшая.
Специально раньше дожидался, когда подъедем к остановке, чтобы сказать «остановите на следующей» :)
Теперь можно водителя потестировать и определить на каком расстоянии какую «следующую» он понимает))
У меня мысль развивалась вот таким образом.

1. Какая разница, какой сегодня день? Следующий вторник — это ближайший.

2. Когда мы (или, в частности, я) подразумеваем действительно ближайший вторник, мы говорим «в этот вторник» или просто «во вторник». Значит, «в следующий вторник» всё-таки означает не ближайший вторник, а через один.

3. «Этот» и «следующий» возможно привязываются к календарной неделе. Например, сегодня четверг. Тогда: «в [этот] понедельник» = -3 дня, «в следующий понедельник» = +4 дня, «в [это] воскресенье» = +3 дня, «в следующее воскресенье» = +10 дней.

4. Я склоняюсь к третьему варианту, но нужно учитывать контекст. Если сегодня среда, и я говорю: «Напомнить в понедельник съесть пирожное», то, очевидно, понедельник текущей недели уже прошёл, значит речь всё-таки о другом понедельнике; в данном случае «в понедельник» = «в следующий понедельник».

Как-то так… :)
Именно так мы ломали стандартную хваталку даты в календарь в айфоне. Там если писать сообщение в imes, то он хватает дату и предлагает занести в календарь. Если дату не писать как «седьмая суббота от сегодня, в час следующий за часом, когда солне в зените».
UFO just landed and posted this here
… последнего мая

Извините, не удержался :)
самое и, в том числе, будет понимать английский язык.

Для английского такое обычно совершенно не нужно. Методы конвертации человекопонятной строки содержащей дату в дату есть во всех язык. Там ноги выросли от сишной getdate.

Конкретно в JS конструктор объекта Date поддерживает строки такого формата.
«Мыть машину в следующий четверг» не понимает
В коде распознается «одиНадцать», хотя по правилам русского языка должно писаться «одиННадцать». Правильное написание «одиннадцать» распознается как «+1 день», а не «+11 дней» (:
Исправил и в коде и в голове. Спасибо.
У меня до сих пор в голове, как учительница в начальных классах объясняла, хитрость как запомнить правописание таких чисел.
Один на дцать, две на дцать, три на дцать, и так далее, единственное исключение четырнадцать.
А почему «один» в мужском роде, а «две» в женском? :)
Спасибо за код!

Рацпредложение: и день, и месяц следует записывать двумя цифрами.
Верно: 05.01.2013.
Неверно: 5.1.2013.
(См. ГОСТ ИСО 8601-2001.)
Там дата передаётся объектом Date(), можете конвертировать её в любой вид. А так не по ГОСТу в дату превращает встроенная функция, использованная в отображении: (new Date()).getLocaleString().
Вообще-то ISO 8601 предписывает YYYY-MM-DD (или то же самое, но без разделителей), если речь про даты, а никак не DD.MM.YYYY.
Да, вы правы насчет стандарта. Но такой порядок записи даты является стандартным для научно-технической документации. Цитата из Мильчина:
Форма дат XX в. в справочных и особо компактных изданиях: 05.08.85 (форма написания в современных документах, кроме научно-технических).
Другие формы: 02.03.1975...
(См. «7.2.1. Даты из числа месяца, порядкового номера месяца и года», ред. 1998 г.)

В любом случае, суть в том, что нули перед однозначными номерами дня/месяца в русском языке убирать не следует.
Может стоит добавить распознавание текста вида «полвторого», «половина двенадцатого», «полпятого вечера», «полседьмого утра»?
Порадовало:
ДР жены. Напомнить за 15 минут.
Не, там:
Напомнить за 15м

что означает: за 15 месяцев.
Вот вам юзкесы:
in: Напомнить про завтрак за 10 минут
out: + завтра [10 ми] | Напомнить за 15 м

in: напомнить про субботник в пятницу
out: субб | Напомнить за 15 м

in: напомнить про вчерашнее завтра
out: + вчера | Напомнить за 15 м
Супер!
Но вот такой пример, некорректно прошел

«Сегодня посмотреть 2 часть Матрицы в 21 час 30 минут »: Распознано как: + сегодня [2 ч 21 30 ми:00]
Интересно! Попробую сделать подобный парсинг в своей библиотечке habrahabr.ru/post/204162/ когда буду в отпуске (в конце декабря). Тоже увлекался в свое время написанием ботов/парсеров текстов, очень интересная тема.
Спасибо за код и идею!
Делал парсинг TimeSpan (не DateTime) на C# для ABCat. Задача — парсинг строки TimeSpan, написанной на русском языке в свободной форме, парсилось время звучания аудиокниги в топиках на рутрекере.
Парсер съедает порядка 99% процентов всех вариантов написания, принятых на рутрекере, правда из-за большого количества записей тяжело оценить сколько из них понимаются правильно. На первый взгляд — большинство. Если кому нужно — спрашивайте или ищите в исходниках на codeplex, строка «public static TimeSpan ToTimeSpan(this string lenght)».
«Дней через пять» велик и могуч русский язык)
Упс. Промазал веткой, показалось что это ответ к моему коменту — сбила с толку ваша прозрачная иконка пользователя.

Такого варианта там нет, ваша правда. Заточено на длительность, и на конкретный сайт. Но добавить поддержку такого варианта совсем несложно. К «примерно» и «около» добавить «через».

31 декабря
Распознано как: 31 дек
Дата: 31.12.2013 17:50:00

32 декабря
Распознано как: 32 дек
Дата: 1.12.2014 17:50:00
Первым делом ввёл не сработавший вариант, который я бы голосом озвучил той же Siri / Google Now: «таймер на полчаса». «Полчаса»/«Час» не работают, «один час» и «тридцать минут» — нормально.

Upd: «будильник завтра в 9 вечера» тоже работает некорректно :)
UFO just landed and posted this here
Перезагрузить сервер в воскресение в 2 распознает как воскр [2:00] — мне кажется, что в повседневной жизни мы скорее оперирует дневными часами, нежели ночными, и следует распознавать такую фразу как 14:00
В повседневной жизни обычно зависит от контекста, как ситуационного («обычно я ложусь в 2, а встаю в 8» — ночь и утро по умолчанию), так и временного («перезагрузить сервер в 2» произнесенное вечером значит в 2 ночи, утром — в 2 дня).
Некоторых увольняют за перезагрузку сервера в 2 дня)
Может он засмотрелся в обед на девушек и случайно перезагрузил сервер :)
«завтра в 7 вечера»
Распознано как: + завтра [7:00]
Дата: 4 Декабрь 2013 г. 7:00:00
Код некрасив в мелочах, несколько примеров:
plus = "-";


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

shablon


Рвет шаблон.

        var matches2 = title.match(/\d{1,4}/g); //все двух-значные цифры


У нас такое не проходит code review. Почему бы не назвать так, чтобы ее имя символизировало ее содержание? digits, numbers, etc.

Не понял, за что комментарию поставили минусы. Вообще-то человек прав, над говнокодом нужно работать.
Еще подкину для теста: «Через две недели в шесть тридцать вечера по Екатеринбургу» и «29 февраля»
«Без пятнадцати полтретьего через четверг напомни купить еды»
Такое не парсит.
Без пятнадцати полтретьего → 14:15
Через четверг → в четверг через неделю

Ну и «Семнадцать минут четвёртого» распознало как «четв».
Бог с ней, с женой, я сам распарсил это небезошибочно :).
Да это просто праздник какой-то!
Удобно встанет в Google Scripts для Google Tasks.
Проверить почту через тридцать минут — + 3 minute

Для решения используйте
.replace(/\s*?три[^дцать]/, " 3")

вместо
.replace(" три", " 3")

Или заменяйте «тридцать», до «трех»
Потрясающе! Выложите на гитхаб!

Не понимает «Вечером сделать ужин жене»
Делал что-то подобное для себя на C# в виде десктопной программы. С хоткеями, напоминалками и т.п. xminderapp.blogspot.ru/2010/01/blog-post.html
Но внутри у меня подход чуть другой: используется грамматика LL с предпросмотром вперед. Просто было интересно, можно ли что-то такое сделать с помощью грамматик и конкретно Coco/R. Парсер занял 90 строк кода на ATG 53605.selcdn.ru/dlxeon_public/habr/ATG/XMinder.ATG потом из него автоматом генерируется C#. Чего нет — так поддержки дат. Но вряд ли уже руки дойдут до этого.

«в субботу в 7 вечера»
Распознано как: субб [7:00]

Хорошо бы в этом случае распознавать как [19:00]
можно скукожить код

if (matches3[0] == «янв») var mymonth = 1;
if (matches3[0] == «фев») var mymonth = 2;
if (matches3[0] == «мар») var mymonth = 3;
if (matches3[0] == «апр») var mymonth = 4;
if (matches3[0] == «мая») var mymonth = 5;
if (matches3[0] == «май») var mymonth = 5;
if (matches3[0] == «июн») var mymonth = 6;
if (matches3[0] == «июл») var mymonth = 7;
if (matches3[0] == «авг») var mymonth = 8;
if (matches3[0] == «сен») var mymonth = 9;
if (matches3[0] == «окт») var mymonth = 10;
if (matches3[0] == «ноя») var mymonth = 11;
if (matches3[0] == «дек») var mymonth = 12;

в

var mymonth = 0, month_list = «янв|фев|мар|апр|ма[яй]|июн|июл|авг|сен|окт|ноя|дек».match(/[^|]+/g);
for (var i=1; i<month_list.length && mymonth === 0; ++i )
if ( new RegExp(month_list[i]).test( matches3[0] ) ) month_num = i+1;
Вот всегда приходится метаться между вариантом где сходу всё понятно, но больше букв и вариантом с красивым кодом и изящностью, но с не мгновенно считываемой логикой.
Неверное у Вас суждение. Можно получить преимущества обеих методов просто изолировав определение номера месяца в функцию:

get_month_number( s: String ) -> Number;

А там уже пиши как хочешь, хоть изящно, хоть «в лоб», хоть с сотней грязных хаков.
Кажется тут надо ввести какие то ограничения на разные варианты фраз и привести пример не верных фраз, которые дадут не корректный ответ.
Либо сделать некое обучения перед использованием.
Sign up to leave a comment.

Articles