如何按分隔符分割字符串,但如果有转义字符则不分割?例如,我有一个字符串:
1|2\|2|3\\|4\\\|4
分隔符是|
,转义的分隔符是\|
。此外,我想忽略转义的反斜杠,所以在\\|
中,|
仍然是分隔符。
因此,对于上述字符串,结果应该是:
[0] => 1
[1] => 2\|2
[2] => 3\\
[3] => 4\\\|4
如何按分隔符分割字符串,但如果有转义字符则不分割?例如,我有一个字符串:
1|2\|2|3\\|4\\\|4
分隔符是|
,转义的分隔符是\|
。此外,我想忽略转义的反斜杠,所以在\\|
中,|
仍然是分隔符。
因此,对于上述字符串,结果应该是:
[0] => 1
[1] => 2\|2
[2] => 3\\
[3] => 4\\\|4
使用黑魔法:
$array = preg_split('~\\\\.(*SKIP)(*FAIL)|\|~s', $string);
\\\\.
匹配反斜杠后面的字符,(*SKIP)(*FAIL)
跳过它,\|
匹配您的分隔符。
我认为比起使用split(...)
函数,更直观的方式是使用一种类似于词法解析器的“扫描”函数。在PHP中,可以使用preg_match_all
函数。你只需要指定匹配:
\
或|
的任何字符\
后面跟着一个\
或|
以下是演示:
$input = "1|2\\|2|3\\\\|4\\\\\\|4";
echo $input . "\n\n";
preg_match_all('/(?:\\\\.|[^\\\\|])+/', $input, $parts);
print_r($parts[0]);
将会打印:
1|2\|2|3\\|4\\\|4
Array
(
[0] => 1
[1] => 2\|2
[2] => 3\\
[3] => 4\\\|4
)
+
可以删除空元素:a||c
。使用*
可能会获得您实际上不想要的空元素(虽然在这里不是)。不知何故,它从来没有完全达到相同的效果... - Kobi[^\\|]
中转义了管道符号。我有点失误。 - Kobi+
改为*
(我猜太多空字符串会被匹配)。如果在管道符之间出现空字符串的边界情况,我会像这样处理它:...|(?<=^|\|)(?=$|\|)
,其中...
是现有的正则表达式。 - Bart Kiers针对未来的读者,这里提供一种通用解决方案。它基于NikiC的想法,使用(*SKIP)(*FAIL)
:
function split_escaped($delimiter, $escaper, $text)
{
$d = preg_quote($delimiter, "~");
$e = preg_quote($escaper, "~");
$tokens = preg_split(
'~' . $e . '(' . $e . '|' . $d . ')(*SKIP)(*FAIL)|' . $d . '~',
$text
);
$escaperReplacement = str_replace(['\\', '$'], ['\\\\', '\\$'], $escaper);
$delimiterReplacement = str_replace(['\\', '$'], ['\\\\', '\\$'], $delimiter);
return preg_replace(
['~' . $e . $e . '~', '~' . $e . $d . '~'],
[$escaperReplacement, $delimiterReplacement],
$tokens
);
}
试一试:
// the base situation:
$text = "asdf\\,fds\\,ddf,\\\\,f\\,,dd";
$delimiter = ",";
$escaper = "\\";
print_r(split_escaped($delimiter, $escaper, $text));
// other signs:
$text = "dk!%fj%slak!%df!!jlskj%%dfl%isr%!%%jlf";
$delimiter = "%";
$escaper = "!";
print_r(split_escaped($delimiter, $escaper, $text));
// delimiter with multiple characters:
$text = "aksd()jflaksd())jflkas(('()j()fkl'()()as()d('')jf";
$delimiter = "()";
$escaper = "'";
print_r(split_escaped($delimiter, $escaper, $text));
// escaper is same as delimiter:
$text = "asfl''asjf'lkas'''jfkl''d'jsl";
$delimiter = "'";
$escaper = "'";
print_r(split_escaped($delimiter, $escaper, $text));
输出:
Array
(
[0] => asdf,fds,ddf
[1] => \
[2] => f,
[3] => dd
)
Array
(
[0] => dk%fj
[1] => slak%df!jlskj
[2] =>
[3] => dfl
[4] => isr
[5] => %
[6] => jlf
)
Array
(
[0] => aksd
[1] => jflaksd
[2] => )jfl'kas((()j
[3] => fkl()
[4] => as
[5] => d(')jf
)
Array
(
[0] => asfl'asjf
[1] => lkas'
[2] => jfkl'd
[3] => jsl
)
注意:存在一个理论级别的问题:implode('::', ['a:', ':b'])
和implode('::', ['a', '', 'b'])
会得到相同的字符串:'a::::b'
。这是一个有趣的implode问题。
最近我想出了一个解决方案:
$array = preg_split('~ ((?<!\\\\)|(?<=[^\\\\](\\\\\\\\)+)) \| ~x', $string);
正则表达式非常慢。更好的方法是在分割字符串之前从字符串中删除转义字符,然后再将它们放回去:
$foo = 'a,b|,c,d||,e';
function splitEscaped($str, $delimiter,$escapeChar = '\\') {
//Just some temporary strings to use as markers that will not appear in the original string
$double = "\0\0\0_doub";
$escaped = "\0\0\0_esc";
$str = str_replace($escapeChar . $escapeChar, $double, $str);
$str = str_replace($escapeChar . $delimiter, $escaped, $str);
$split = explode($delimiter, $str);
foreach ($split as &$val) $val = str_replace([$double, $escaped], [$escapeChar, $delimiter], $val);
return $split;
}
print_r(splitEscaped($foo, ',', '|'));
该函数基于逗号进行分割,但如果使用“|”进行转义,则不会进行分割。它还支持双重转义,因此在分割后,“||”将变为单个“|”:
Array ( [0] => a [1] => b,c [2] => d| [3] => e )
(*SKIP)(*FAIL)
有文档说明吗? - eyelidlessnessa\\\a
将会变成 两个 反斜杠 ;) 对于 PHP 来说,a\\\a
和a\\\\a
是相同的 :) - NikiC\K
转义序列。请参见demo。 - HamZa