Комментарии 77
Классно получилось, спасибо! Форкнул, на досуге попробую сделать такое на PHP :)
Делал подобный парсер на php для своей домашнего xmpp-бота. Бот умел напоминать о событиях, делать записи в гугл-календарь и прочие приятные мелочи.
Так вот для php мне показалось удобнее использовать готовую функцию strtotime.
Чтобы обучить strtotime парсить даты на русском языке, просто переводил ключевые слова с русского на английский.
Вот так:
Работает неплохо.
Так вот для php мне показалось удобнее использовать готовую функцию strtotime.
Чтобы обучить strtotime парсить даты на русском языке, просто переводил ключевые слова с русского на английский.
Вот так:
$keywords = array(
"понедельник" => "monday",
"воскресенье" => "sunday",
"послезавтра" => "+2 days",
"месяцев" => "months",
"вторник" => "tuesday",
...
$result = strtotime(
str_replace(array_keys($keywords),
array_values($keywords),
self::strtolower($time) )
);
Работает неплохо.
Мой код на PHP в 448 строк, генерирующий числительное в любом падеже: 123 => «сто двадцать три». Не пинайте особо, коду больше лет, чем коммиту. Передаю этот код в общественное достояние. Может быть, кому-то пригодится.
«Проснуться 2 января» выдает 2 января 2013 года.
Еще неплохо было бы, если бы распознавало что-то типа «7 вечера».
Еще неплохо было бы, если бы распознавало что-то типа «7 вечера».
У вас баг в распозновании «двадцать» и т.д., а «двадцать пять», я так понял вообще не реализовано.
Баг фиксится прсто — replace двадцати надо поставить раньше replace'а двух.
Баг фиксится прсто — replace двадцати надо поставить раньше replace'а двух.
«в 21часов 30 минут» — о_О
А если написать «Сегодня в 21час 30 минут», то получим «Распознано как: + сегодня [21 30 ми:00]»
Ну, и сам код причиняет боль, да…
А если написать «Сегодня в 21час 30 минут», то получим «Распознано как: + сегодня [21 30 ми:00]»
Ну, и сам код причиняет боль, да…
«днюха в следующий вторник»
«Третий четверг каждого нечётного месяца — санитарный день»
Мы, кстати, спорили с женой, если сегодня понедельник, то что означает фраза: «следующий вторник»: завтрашний вторник, или через 8 дней?
Не могу не вспомнить людей, говорящих фразу «на той неделе». У меня она ассоциируется с прошлой неделей, а у энного количества людей старшего поколения по каким-то неведомым мне причинам со следующей.
Потому что «та» — противоположность «этой». «На той неделе» = «На другой неделе».
А «другая» — это какая неделя, прошлая или будущая?
Проблема не в неопределенности — тут, вы правы, контекст решает. Проблема исключительно холиварного характера — вроде бы и правильно, но все равно коробит, когда будущую неделю называют «той».
— А у Васи день рождения уже прошел или будет только?
— На той неделе.
— На той неделе.
Думаю, тут как с «остановите на следующей остановке» — чем ближе к остановке это сказано, тем менее вероятно что имеется ввиду ближайшая.
У меня мысль развивалась вот таким образом.
1. Какая разница, какой сегодня день? Следующий вторник — это ближайший.
2. Когда мы (или, в частности, я) подразумеваем действительно ближайший вторник, мы говорим «в этот вторник» или просто «во вторник». Значит, «в следующий вторник» всё-таки означает не ближайший вторник, а через один.
3. «Этот» и «следующий» возможно привязываются к календарной неделе. Например, сегодня четверг. Тогда: «в [этот] понедельник» = -3 дня, «в следующий понедельник» = +4 дня, «в [это] воскресенье» = +3 дня, «в следующее воскресенье» = +10 дней.
4. Я склоняюсь к третьему варианту, но нужно учитывать контекст. Если сегодня среда, и я говорю: «Напомнить в понедельник съесть пирожное», то, очевидно, понедельник текущей недели уже прошёл, значит речь всё-таки о другом понедельнике; в данном случае «в понедельник» = «в следующий понедельник».
Как-то так… :)
1. Какая разница, какой сегодня день? Следующий вторник — это ближайший.
2. Когда мы (или, в частности, я) подразумеваем действительно ближайший вторник, мы говорим «в этот вторник» или просто «во вторник». Значит, «в следующий вторник» всё-таки означает не ближайший вторник, а через один.
3. «Этот» и «следующий» возможно привязываются к календарной неделе. Например, сегодня четверг. Тогда: «в [этот] понедельник» = -3 дня, «в следующий понедельник» = +4 дня, «в [это] воскресенье» = +3 дня, «в следующее воскресенье» = +10 дней.
4. Я склоняюсь к третьему варианту, но нужно учитывать контекст. Если сегодня среда, и я говорю: «Напомнить в понедельник съесть пирожное», то, очевидно, понедельник текущей недели уже прошёл, значит речь всё-таки о другом понедельнике; в данном случае «в понедельник» = «в следующий понедельник».
Как-то так… :)
Именно так мы ломали стандартную хваталку даты в календарь в айфоне. Там если писать сообщение в imes, то он хватает дату и предлагает занести в календарь. Если дату не писать как «седьмая суббота от сегодня, в час следующий за часом, когда солне в зените».
самое и, в том числе, будет понимать английский язык.
Для английского такое обычно совершенно не нужно. Методы конвертации человекопонятной строки содержащей дату в дату есть во всех язык. Там ноги выросли от сишной getdate.
Конкретно в JS конструктор объекта Date поддерживает строки такого формата.
«Мыть машину в следующий четверг» не понимает
В коде распознается «одиНадцать», хотя по правилам русского языка должно писаться «одиННадцать». Правильное написание «одиннадцать» распознается как «+1 день», а не «+11 дней» (:
Исправил и в коде и в голове. Спасибо.
Спасибо за код!
Рацпредложение: и день, и месяц следует записывать двумя цифрами.
Верно: 05.01.2013.
Неверно: 5.1.2013.
(См. ГОСТ ИСО 8601-2001.)
Рацпредложение: и день, и месяц следует записывать двумя цифрами.
Верно: 05.01.2013.
Неверно: 5.1.2013.
(См. ГОСТ ИСО 8601-2001.)
Там дата передаётся объектом Date(), можете конвертировать её в любой вид. А так не по ГОСТу в дату превращает встроенная функция, использованная в отображении: (new Date()).getLocaleString().
Да, вы правы насчет стандарта. Но такой порядок записи даты является стандартным для научно-технической документации. Цитата из Мильчина:
В любом случае, суть в том, что нули перед однозначными номерами дня/месяца в русском языке убирать не следует.
Форма дат XX в. в справочных и особо компактных изданиях: 05.08.85 (форма написания в современных документах, кроме научно-технических).(См. «7.2.1. Даты из числа месяца, порядкового номера месяца и года», ред. 1998 г.)
Другие формы: 02.03.1975...
В любом случае, суть в том, что нули перед однозначными номерами дня/месяца в русском языке убирать не следует.
Судя по всему, необходимый документ — это ГОСТ Р 6.30-2003 (п. 3.11).
Может стоит добавить распознавание текста вида «полвторого», «половина двенадцатого», «полпятого вечера», «полседьмого утра»?
Порадовало:
ДР жены. Напомнить за 15 минут.
Супер!
Но вот такой пример, некорректно прошел
«Сегодня посмотреть 2 часть Матрицы в 21 час 30 минут »: Распознано как: + сегодня [2 ч 21 30 ми:00]
Но вот такой пример, некорректно прошел
«Сегодня посмотреть 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)».
Парсер съедает порядка 99% процентов всех вариантов написания, принятых на рутрекере, правда из-за большого количества записей тяжело оценить сколько из них понимаются правильно. На первый взгляд — большинство. Если кому нужно — спрашивайте или ищите в исходниках на codeplex, строка «public static TimeSpan ToTimeSpan(this string lenght)».
«Дней через пять» велик и могуч русский язык)
31 декабря
32 декабря
Распознано как: 31 дек
Дата: 31.12.2013 17:50:00
32 декабря
Распознано как: 32 дек
Дата: 1.12.2014 17:50:00
Первым делом ввёл не сработавший вариант, который я бы голосом озвучил той же Siri / Google Now: «таймер на полчаса». «Полчаса»/«Час» не работают, «один час» и «тридцать минут» — нормально.
Upd: «будильник завтра в 9 вечера» тоже работает некорректно :)
Upd: «будильник завтра в 9 вечера» тоже работает некорректно :)
Перезагрузить сервер в воскресение в 2 распознает как воскр [2:00] — мне кажется, что в повседневной жизни мы скорее оперирует дневными часами, нежели ночными, и следует распознавать такую фразу как 14:00
«завтра в 7 вечера»
Распознано как: + завтра [7:00]
Дата: 4 Декабрь 2013 г. 7:00:00
Распознано как: + завтра [7:00]
Дата: 4 Декабрь 2013 г. 7:00:00
Код некрасив в мелочах, несколько примеров:
я не ожидаю получить минус в переменной, которая называется плюс. Почему бы не назвать ее modifier, например?
Рвет шаблон.
У нас такое не проходит code review. Почему бы не назвать так, чтобы ее имя символизировало ее содержание? digits, numbers, etc.
plus = "-";
я не ожидаю получить минус в переменной, которая называется плюс. Почему бы не назвать ее modifier, например?
shablon
Рвет шаблон.
var matches2 = title.match(/\d{1,4}/g); //все двух-значные цифры
У нас такое не проходит code review. Почему бы не назвать так, чтобы ее имя символизировало ее содержание? digits, numbers, etc.
Еще подкину для теста: «Через две недели в шесть тридцать вечера по Екатеринбургу» и «29 февраля»
«Без пятнадцати полтретьего через четверг напомни купить еды»
Такое не парсит.
Такое не парсит.
Да это просто праздник какой-то!
Удобно встанет в Google Scripts для Google Tasks.
Удобно встанет в 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#. Чего нет — так поддержки дат. Но вряд ли уже руки дойдут до этого.
Но внутри у меня подход чуть другой: используется грамматика LL с предпросмотром вперед. Просто было интересно, можно ли что-то такое сделать с помощью грамматик и конкретно Coco/R. Парсер занял 90 строк кода на ATG 53605.selcdn.ru/dlxeon_public/habr/ATG/XMinder.ATG потом из него автоматом генерируется C#. Чего нет — так поддержки дат. Но вряд ли уже руки дойдут до этого.
«в субботу в 7 вечера»
Распознано как: субб [7:00]
Хорошо бы в этом случае распознавать как [19:00]
Распознано как: субб [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;
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;
Вот всегда приходится метаться между вариантом где сходу всё понятно, но больше букв и вариантом с красивым кодом и изящностью, но с не мгновенно считываемой логикой.
Кажется тут надо ввести какие то ограничения на разные варианты фраз и привести пример не верных фраз, которые дадут не корректный ответ.
Либо сделать некое обучения перед использованием.
Либо сделать некое обучения перед использованием.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
250 строк кода, распознающих дату на русском языке