Comments 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;
Вот всегда приходится метаться между вариантом где сходу всё понятно, но больше букв и вариантом с красивым кодом и изящностью, но с не мгновенно считываемой логикой.
Кажется тут надо ввести какие то ограничения на разные варианты фраз и привести пример не верных фраз, которые дадут не корректный ответ.
Либо сделать некое обучения перед использованием.
Либо сделать некое обучения перед использованием.
Sign up to leave a comment.
250 строк кода, распознающих дату на русском языке