preg_match_all远程内容

时间:2017-02-10 08:51:08

标签: php regex file-get-contents

我尝试解析iCal:

    //open file
    $calendar = file_get_contents('http://app.kigo.net/public/ics.php?c-7ca2eb67c1a7fa8b87b2434ed1096076-422-9871b35967bb29f999cd11ac72943011');
    //debug purpose
    echo $calendar;
    //parse string
    preg_match_all('#^BEGIN\:VEVENT.*?END\:VEVENT$#sm',$calendar,$results,PREG_SET_ORDER);
    //output: empty!
    print_r($results);

它返回一个空数组。

无论如何,如果我复制/粘贴" $ calendar"在另一个变量上的内容,并用相同的正则表达式解析它,它工作正常。

为什么当我直接从file_get_contents调用相同字符串上的preg_match_all时,它的工作错误了?

1 个答案:

答案 0 :(得分:1)

远程文件使用序列CR LF作为换行符,这就是锚$不匹配的原因。当您将文件内容复制/粘贴到默认情况下仅使用LF作为换行符的应用程序时,序列CR LF可能会默默地替换为LF并且您的模式可以正常工作。

解决问题的几种方法:

1)在你的模式中明确写出回车符:

#^BEGIN:VEVENT.*?END:VEVENT\r$#sm

如果您不希望在比赛结束时回车,请使用trim或将其置于先行断言:#^BEGIN:VEVENT.*?END:VEVENT(?=\r$)#sm。 您也可以删除$并使用与\R\r\r\n匹配的\n别名。

2)允许$使用指令(*ANYCRLF)

匹配任何换行序列
#(*ANYCRLF)^BEGIN:VEVENT.*?END:VEVENT$#sm

3)不要在所有上使用图案(毕竟你只是在固定线之间寻找块,如果你的文件可能有点长,它会更优雅并节省内存按行读取文件并使用生成器返回块)

$filePath = 'http://app.kigo.net/public/ics.php?c-7ca2eb67c1a7fa8b87b2434ed1096076-422-9871b35967bb29f999cd11ac72943011';

try {
    if ( false === $fp = fopen($filePath, 'rb') )
        throw new Exception('Could not open the file!');

} catch (Exception $e) {
    echo 'Error (File: ' . $e->getFile() . ', line ' . $e->getLine() . '): ' . $e->getMessage();
}

foreach (genBlocks($fp, "BEGIN:VEVENT\r\n", "END:VEVENT\r\n") as $block) {
    echo $block . PHP_EOL;
}

fclose($fp);

function genBlocks($fp, $start, $end, $buffer = 1024) {
    $block = false;
    while ( false !== $line = fgets($fp, $buffer) ) {
        if ( $line === $start ) {
            $block = $line;
        } elseif ( $block !== false ) {
            $block .= $line;
            if ( $line === $end ) {
                yield $block;
                $block = false;
            }
        }
    }
}

注意:您也可以使用stream_get_line代替fgets,因为此代码能够返回没有换行符序列的行。