PHP:爆炸但忽略转义分隔符

时间:2011-12-15 12:00:24

标签: php

我有一个flatfile数据库,它是由分隔符分隔的数据。

我允许人们在他们的输入中使用分隔符,但我确保事先用\转义它。

问题是我的explode()函数仍然试图拆分转义的分隔符,所以如何告诉它忽略它们?

5 个答案:

答案 0 :(得分:8)

请改用preg_split。通过使用正则表达式,只有在前缀为反斜杠的情况下才能匹配分隔符。

编辑:

preg_split('~(?<!\\\)' . preg_quote($delimeter, '~') . '~', $text);

答案 1 :(得分:2)

这里的解决方案都没有正确处理任意数量的转义字符,或者它们将它们留在输出中。这是另一种选择:

function separate($string, $separator = '|', $escape = '\\') {
    if (strlen($separator) != 1 || strlen($escape) != 1) {
        trigger_error(__FUNCTION__ . ' requires delimiters to be single characters.', E_USER_WARNING);
        return;
    }
    $segments = [];
    $string = (string) $string;
    do {
        $segment = '';
        do {
            $segment_length = strcspn($string, "$separator$escape");
            if ($segment_length) {
                $segment .= substr($string, 0, $segment_length);
            }
            if (strlen($string) <= $segment_length) {
                $string = null;
                break;
            }
            if ($escaped = $string[$segment_length] == $escape) {
                $segment .= (string) substr($string, ++$segment_length, 1);
            }
            $string = (string) substr($string, ++$segment_length);
        } while ($escaped);
        $segments[] = $segment;
    } while ($string !== null);
    return $segments;
}

这会将foo\|ba\r\\|baz|之类的原始字符串处理为foo|bar\baz和一个空字符串。

如果要在输出中保留转义字符,则必须修改该函数。

注意:如果您使用mb function overloading,则会出现不可预测的行为。

答案 2 :(得分:0)

输入数据

key1=val1;key2=val2start\;val2end;key3=val3\\;key4=val4\\\;key5=val5\\\\;key6=val6

REGEX

/(.*?[^\\](\\\\)*?);/

实施例

<?php

$data="key1=val1;key2=val2start\\;val2end;key3=val3\\\\;key4=val4\\\\\\;key5=val5\\\\\\\\;key6=val6";

$regex='/(.*?[^\\\\](\\\\\\\\)*?);/';
preg_match_all($regex, $data.';', $matches);

print_r($matches[1]);

输出

Array
(
    [0] => key1=val1
    [1] => key2=val2start\;val2end
    [2] => key3=val3\\
    [3] => key4=val4\\\;key5=val5\\\\
    [4] => key6=val6
)

答案 3 :(得分:0)

您将发现此解决方案比对大字符串使用正则表达式更有用。我使用一个流来允许使用fgetcsv,该流已针对此类情况进行了优化。

<?php
function escaped_explode($string,$delimit,$escape=NULL,$enclosure=NULL,$max_line_length=0){
    $r=[];
    $stream = fopen('php://memory','r+');
    fwrite($stream, $string);
    rewind($stream);
    while (($data = fgetcsv($stream,$max_line_length,$delimit,$enclosure,$escape)) !== FALSE)
        $r=array_merge($r,$data);
    fclose($stream);
    return $r;
}
?>

用法:

$pipelined_values = escaped_explode($source,'|','\\');

这也很方便,因为您可以选择使用诸如引号之类的附件,而不是仅使用转义字符。如果您遇到解析某人的JSON值或其他语法的斑点的感觉,那么这很好,因为您可以同时封装和转义。

$source= <<<JSON
'{ "key":"val", "n":0}', 
'{ "key":"val", "n":1, "name": "French du\'Name" }',
'{ "key":"val", "n":2}'
JSON;

可以解释

<?php
$objects=[];
$raw= escaped_explode($source, ',', '\\', "'");
foreach($raw as $r)
    $objects[] = json_decode($r);
?>

答案 4 :(得分:-1)

preg_split肯定会给你你想要的东西。但是,我写了这个小片段,它似乎运作良好。如果你不想使用preg_split,请点击:

$delim = ',';
$string = "Hello,World,This\\,is escaped,This is not";
$array = explode($delim, $string);
$finalArray = array();
var_dump($array);
for($i = 0; $i < count($array); $i++) {

    $strToPush = $array[$i];

    while("\\" == substr($array[$i], -1)) {
        $i++;
        $strToPush .= $delim . $array[$i];

    }
    array_push($finalArray, $strToPush); 

}
var_dump($finalArray);

给出了输出:

  

阵列
    0 =&gt;字符串&#39;您好&#39; (长度= 5)
    1 =&gt; string&#39; World&#39; (长度= 5)
    2 =&gt;字符串&#39;这个&#39; (长度= 5)
    3 =&gt;字符串&#39;被转义&#39; (长度= 10)
    4 =&gt;字符串&#39;这不是&#39; (长度= 11)

     

阵列
    0 =&gt;字符串&#39;您好&#39; (长度= 5)
    1 =&gt; string&#39; World&#39; (长度= 5)
    2 =&gt;字符串&#39;这个\,被转义&#39; (长度= 16)
    3 =&gt;字符串&#39;这不是&#39; (长度= 11)