编写正则表达式来处理来自外部源的时间集

时间:2011-04-05 14:42:29

标签: php regex

我正在编写一个从外部世界获取数据的脚本,我正在查看事件发生的时间,例如两组时间:

Mon - Fri: 12:00 - 14:00, 18:00 - 22:30, Sat: 18:00 - 22:00
Tue, Wed, Thu: 17:30 - 23:00, Sat: 12:00 - 17:00, Sun: 17:00 - 22:30

正如您所看到的,在每种情况下,数据都以不同的方式显示(mon-fri或tues,wed,thu)。任何人都可以给我一些关于编写正则表达式/处理形式的指针,以便将数据转换为数组,例如:

$timing['mon'][1]['start'] = '12:00';
$timing['mon'][1]['finish'] = '14:00';
$timing['mon'][2]['start'] = '18:00';
$timing['mon'][2]['finish'] = '22:30';

提前致谢..

3 个答案:

答案 0 :(得分:1)

<?php

$string = "Mon - Fri: 12:00 - 14:00, 18:00 - 22:30, Sat: 18:00 - 22:00
Tue, Wed, Thu: 17:30 - 23:00, Sat: 12:00 - 17:00, Sun: 17:00 - 22:30";

preg_match_all("/([a-zA-Z\-\s\,]+): ([0-9\:\,\s\-]+)/", $string, $matches, PREG_OFFSET_CAPTURE);

$data = array();

foreach ($matches[1] as $key => $day){

    //Split the data and remove whitespace.
    $values = explode(",", $matches[2][$key][0]);
    foreach ($values as $a => $b) $values[$a] = trim($b); if (empty($values[$a])) unset($values[$a]);

    //Loop each set and split the stand and end.
    foreach ($values as $a => $b){

        $splits = explode("-", $b);
        $values[$a] = array("Start" => $splits[0], "End" => $splits[1]);

    } //end foreach

    //Place the new data in the array.
    $data[trim($day[0])] = $values;

} //end foreach

echo "<pre>";
print_r($data);

?>

上面的代码将允许在您的数据中进行更改,正如您将注意到的,数组中的键将根据您的数据保留为“Mon-Fri”,因为它的格式没有标准,出现它无论如何都会发生变化。

答案 1 :(得分:1)

以为我会好好享受。

我假设两条不同的线是两个不同的输入。并没有真正困扰错误检查。因此,如果格式与您提供的样本有很大不同,则很可能会失败。

<?php
/**
 * Gets the days of the week in a range. e.g. given Mon Wed, will return an
 * array of Mon, Tue, Wed
 * @param string $start 3 letter day of the week (ucfirst)
 * @param string $end 3 letter day of the week (ucfirst)
 * @return array The days from $start to $end 
 */
function get_day_range($start, $end) {
    if ($start == $end)
        return array($start);

    $date = new DateTime($start);
    $days = array($start);
    while($date->format('D') != $end){
        $date->modify('+1 day');
        $days[] = $date->format('D');
    }

    return $days;
}

/**
 * Checks if the needle exists in the haystack
 * @param string $needle
 * @param string $haystack
 * @return bool 
 */
function instr($needle, $haystack) {
    return strpos($haystack, $needle) !== false;
}

function get_event_times($input) {
    preg_match_all('/
        (?<days>(
            (Mon|Tue|Wed|Thu|Fri|Sat|Sun)
            \s*[-,]?\s*
        )+):\s
        (?<times>
            (
                (
                    \d\d:\d\d
                        \s-\s
                    \d\d:\d\d
                ),?\s*
            )+
        )/x', $input, $matches, PREG_SET_ORDER);
    $return = array();

    foreach($matches as $match) {
        $days = $match['days'];

        // Is a day range
        if (instr(' - ', $days)) {
            list($start, $end) = explode(' - ', $days, 2);
            $days = get_day_range($start, $end);
        }

        // Is a list of days
        elseif (instr(', ', $days)) {
            $days = explode(', ', $days);
        }

        // Is just one day
        else {
            $days = array($days);
        }

        $times = trim($match['times'], ', ');
        $times = explode(', ', $times);

        foreach($days as $day) {
            foreach($times as $time) {
                list($start, $end) = explode(' - ', $time);
                $return[$day][] = array(
                    'start' => $start,
                    'end' => $end
                );
            }
        }
    }
    return $return;
}

$inputs = array(
    'Mon - Fri: 12:00 - 14:00, 18:00 - 22:30, Sat: 18:00 - 22:00',
    'Tue, Wed, Thu: 17:30 - 23:00, Sat: 12:00 - 17:00, Sun: 17:00 - 22:30'
);

foreach($inputs as $input) {
    var_dump(get_event_times($input));
}

答案 2 :(得分:0)

你需要的不仅仅是一个正则表达式来解决这个问题。我首先将它分解成更小的块。由于分隔符以该格式提供双重(或三重)任务,因此您不能仅沿着分隔符将其分解,因此您需要以块的形式处理它。首先,我会在第一次结肠之前和之后打破一切。第一部分是你的日期说明符,所以解析它 - 如果它是逗号分隔列表,只需将其拆分为键列表。如果是范围,请使用循环来构建密钥列表。之后,您有一个时间列表。我会循环类似\d\d:\d\d - \d\d:\d\d,?的东西,直到它无法匹配(指示行尾或另一个条目),将每个间隔应用于您之前生成的密钥集,并在那里使用第二个索引的增量计数。一旦该模式无法匹配,重新开始整个过程​​:

  1. 分割到第一个冒号以获取日期说明符
  2. 将日期说明符处理为列表或范围(在短划线上匹配可能会告诉您具有哪种情况)到日期列表中
  3. 接受下一个逗号或字符串的结尾(或使用时间范围模式)以获取时间范围
  4. 将该时间范围应用于第2步的日期列表
  5. 如果有另一个时间模式,则循环回3,否则循环回1