Разбор строки для дат в PHP - PullRequest
22 голосов
/ 16 июня 2010

Учитывая произвольную строку, например ("I'm going to play croquet next Friday" или "Gadzooks, is it 17th June already?"), как бы вы пошли извлекать даты оттуда?

Если это выглядит как хороший кандидат для слишком сложногокорзина, возможно, вы могли бы предложить альтернативу.Я хочу иметь возможность анализировать сообщения в Твиттере по датам.Я смотрю твиты, которые пользователи направляют на этот сервис, поэтому их можно научить использовать более простой формат, однако я бы хотел, чтобы он был максимально прозрачным.Есть ли хорошая середина, о которой вы могли бы подумать?

Ответы [ 9 ]

12 голосов
/ 16 июня 2010

Если у вас есть лошадиные силы, вы можете попробовать следующий алгоритм.Я показываю пример и оставляю вам утомительную работу:)

//Attempt to perform strtotime() on each contiguous subset of words...

//1st iteration
strtotime("Gadzooks, is it 17th June already")
strtotime("is it 17th June already")
strtotime("it 17th June already")
strtotime("17th June already")
strtotime("June already")
strtotime("already")

//2nd iteration
strtotime("Gadzooks, is it 17th June")
strtotime("is it 17th June")
strtotime("17th June") //date!
strtotime("June") //date!

//3rd iteration
strtotime("Gadzooks, is it 17th")
strtotime("is it 17th")
strtotime("it 17th")
strtotime("17th") //date!

//4th iteration
strtotime("Gadzooks, is it")
//etc

И мы можем предположить, что strtotime("17th June") является более точным, чем strtotime("17th"), просто потому, что содержит больше слов ...то есть «следующая пятница» всегда будет более точной, чем «пятница».

6 голосов
/ 16 июня 2010

Я бы сделал это так:

Сначала проверьте, является ли вся строка действительной датой, с помощью strtotime (). Если так, то все готово.

Если нет, определите, сколько слов в вашей строке (например, разделить на пробел). Пусть это число будет n.

Переберите каждую n-1 комбинацию слов и используйте strtotime (), чтобы увидеть, является ли фраза допустимой датой. Если да, то вы нашли самую длинную действительную строку даты в вашей исходной строке.

Если нет, переберите каждую n-2 комбинацию слов и используйте strtotime (), чтобы увидеть, является ли фраза допустимой датой. Если это так, вы нашли самую длинную действительную строку даты в вашей исходной строке.

... и так далее, пока вы не найдете правильную строку с датой или не найдете каждое отдельное / отдельное слово. Найдя самые длинные совпадения, вы получите самые информированные даты (если это имеет смысл). Поскольку вы имеете дело с твитами, ваши строки никогда не будут огромными.

2 голосов
/ 23 сентября 2016

Основываясь на предложении Дольфа , я выписал функцию, которая, я думаю, служит цели.

public function parse_date($text, $offset, $length){

  $parseArray = preg_split( "/[\s,.]/", $text);
  $dateTest = implode(" ", array_slice($parseArray, $offset, $length == 0 ? null : $length));

  $date = strtotime($dateTest);

  if ($date){
    return $date;
  }

  //make the string one word shorter in the front
  $offset++;

  //have we reached the end of the array?
  if($offset > count($parseArray)){

    //reset the start of the string
    $offset = 0;

    //trim the end by one
    $length--;

    //reached the very bottom with no date found
    if(abs($length) >= count($parseArray)){
      return false;
    }
  }

  //try to find the date with the new substring
  return $this->parse_date($text, $offset, $length);
}

Вы бы назвали это так:

parse_date («Настройка даты 5 января 2017 г.», 0, 0)

2 голосов
/ 17 июня 2010

Следуя Dolph Mathews идее и фактически игнорируя мой предыдущий ответ, я создал довольно хорошую функцию, которая делает именно это.Он возвращает строку, которая, по его мнению, соответствует дате, ее дате в Unix и самой дате либо с указанным пользователем форматом, либо с предопределенным (F j, Y). Я написал небольшой пост об этом на Извлечение даты из строки с помощью PHP .В качестве тизера, вот вывод двух примеров строк:

Ввод : «Я собираюсь сыграть в крокет в следующую пятницу»

Output: Array ( 
           [string] => "next friday",
           [unix] => 1276844400,
           [date] => "June 18, 2010" 
        )

Ввод : «Гадзукс, уже 17 июня?»

Output: Array ( 
           [string] => "17th june",
           [unix] => 1276758000,
           [date] => "June 17, 2010" 
        )

Надеюсь, это кому-нибудь поможет.

2 голосов
/ 16 июня 2010

Используйте функцию strtotime php.

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

Например, он может принимать строки типа «следующая пятница» и «15 июня» и возвращать соответствующую метку времени UNIX для даты в строке. Я предполагаю, что если вы рассмотрите некоторые основные правила, такие как поиск «следующего X» и названий недель и месяцев, вы сможете сделать это.

Если бы вы могли найти " в следующую пятницу " из " Я собираюсь сыграть в крокет в следующую пятницу ", вы можете извлечь дату. Похоже, интересный проект! Но имейте в виду, что strtotime принимает только английские фразы и не будет работать с любым другим языком.

Например, правило, которое будет определять местонахождение всех случаев «Следующий будний день», будет просто:

$datestring = "I'm going to play croquet next Friday";

$weekdays = array('monday','tuesday','wednesday',
                  'thursday','friday','saturday','sunday');

foreach($weekdays as $weekday){
    if(strpos(strtolower($datestring),"next ".$weekday) !== false){
        echo date("F j, Y, g:i a",strtotime("next ".$weekday));
    }
}

Это вернет дату следующего дня недели, упомянутую в строке, если она следует правилу! В данном конкретном случае выходной сигнал был June 18, 2010, 12:00 am. Используя несколько (может быть, больше, чем несколько!) Из этих правил, вы, скорее всего, извлечете правильную дату в большом проценте случаев, учитывая, что пользователи используют правильное написание.

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

1 голос
/ 06 апреля 2019

Вдохновленный неработающей связью Хуана Кортеса, основанной на алгоритме Дольфа, я пошел дальше и сам написал ее.Обратите внимание, что я решил просто вернуться в первый успешный матч.

<?php
function extractDatetime($string) {
    if(strtotime($string)) return $string;
    $string = str_replace(array(" at ", " on ", " the "), " ", $string);
    if(strtotime($string)) return $string;

    $list = explode(" ", $string);
    $first_length = count($list);
    for($j=0; $j < $first_length; $j++) {
        $original_length = count($list);
        for($i=0; $i < $original_length; $i++) {
            $temp_list = $list;
            for($k = 0; $k < $i; $k++) unset($temp_list[$k]);
            //echo "<code>".implode(" ", $temp_list)."</code><br/>"; // for visualizing the tests, if you want to see it
            if(strtotime(implode(" ", $temp_list))) return implode(" ", $temp_list);
        }
        array_pop($list);
    }

    return false;
}

Входы

$array = array(
        "Gadzooks, is it 17th June already",
        "I’m going to play croquet next Friday",
        "Where was the dog yesterday at 6 PM?",
        "Where was Steve on Monday at 7am?"
);

foreach($array as $a) echo "$a => ".extractDatetime(str_replace("?", "", $a))."<hr/>";

Выходы

Gadzooks, is it 17th June already
is it 17th June already
it 17th June already
17th June already
June already
already
Gadzooks, is it 17th June
is it 17th June
it 17th June
17th June
Gadzooks, is it 17th June already => 17th June
-----
I’m going to play croquet next Friday
going to play croquet next Friday
to play croquet next Friday
play croquet next Friday
croquet next Friday
next Friday
I’m going to play croquet next Friday => next Friday
-----
Where was Rav Four yesterday 6 PM
was Rav Four yesterday 6 PM
Rav Four yesterday 6 PM
Four yesterday 6 PM
yesterday 6 PM
Where was the Rav Four yesterday at 6 PM? => yesterday 6 PM
-----
Where was Steve Monday 7am
was Steve Monday 7am
Steve Monday 7am
Monday 7am
Where was Steve on Monday at 7am? => Monday 7am
-----
1 голос
/ 17 июня 2010

То, что вы ищете, - это синтаксический анализатор временных выражений.Вы можете посмотреть статью Википедии , чтобы начать.Имейте в виду, что анализаторы могут быть довольно сложными, потому что это действительно проблема распознавания языка.Обычно это проблема, решаемая в области искусственного интеллекта / компьютерной лингвистики.

1 голос
/ 16 июня 2010

Большинство предложенных алгоритмов на самом деле довольно слабые.Я предлагаю использовать несколько хороших регулярных выражений для дат и проверки предложения с ним.Используйте это в качестве примера:

(\d{1,2})? 
((mon|tue|wed|thu|fri|sat|sun)|(monday|tuesday|wednesday|thursday|friday|saturday|sunday))?
(\d{1,2})? (\d{2,4})?

Я пропустил месяцы, поскольку не уверен, что помню их в правильном порядке.

Это самое простое решение, но я сделаюработа лучше, чем другие решения на основе вычислительной мощности.(И да, это вряд ли безошибочное регулярное выражение, но вы поняли).Затем примените функцию strtotime к соответствующей строке.Это самое простое и быстрое решение.

1 голос
/ 16 июня 2010

Что-то вроде следующего может сделать это:

$months = array(
                    "01" => "January", 
                    "02" => "Feberuary", 
                    "03" => "March", 
                    "04" => "April", 
                    "05" => "May", 
                    "06" => "June", 
                    "07" => "July", 
                    "08" => "August", 
                    "09" => "September", 
                    "10" => "October", 
                    "11" => "November", 
                    "12" => "December"
                );

$weekDays = array(
                    "01" => "Monday", 
                    "02" => "Tuesday", 
                    "03" => "Wednesday", 
                    "04" => "Thursday", 
                    "05" => "Friday", 
                    "06" => "Saturday", 
                    "07" => "Sunday"
                );

foreach($months as $value){
    if(strpos(strtolower($string),strtolower($value))){
        \\ extract and assign as you like...
    }
}

Возможно, сделайте цикл nother для проверки других дней недели или других форматов, или просто вложите.

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