爆炸但忽略转义分隔符


PHP: explode but ignore escaped delimiter

我有一个平面文件数据库,它是由分隔符分隔的数据。

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

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

使用preg_split代替。通过使用正则表达式,只有当分隔符前面没有反斜杠时,才能匹配分隔符。

编辑:

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

这里的解决方案都不能正确处理任何数量的转义字符,或者将它们留在输出中。这是另一个选项:

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函数重载,这将有不可预测的行为。

输入数据

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
)

您会发现这个解决方案比使用regex处理大字符串更有用。我使用了一个流来允许使用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);
?>