【问题标题】:PHP regex for matching double and/or single quoted strings within in a string用于匹配字符串中的双引号和/或单引号字符串的 PHP 正则表达式
【发布时间】:2010-07-10 16:55:44
【问题描述】:

我正在开发一个模板类,但在尝试从字符串参数列表中解析出带引号的字符串列表时遇到了问题。以字符串为例:

$string = 'VAR_SELECTED, \'Hello m\'lady\', "null"';

我在提出一个提取字符串“Hello m'lady”和“null”的正则表达式时遇到问题。我得到的最接近的是

$string = 'VAR_SELECTED, \'Hello m\'lady\', "null", \'TE\'ST\'';
preg_match_all('/(?:[^\']|\\\\.)+|(?:[^"]|\\\\.)+/', $string, $matches);
print_r($matches);

哪些输出:

Array
(
    [0] => Array
        (
            [0] => VAR_SELECTED, 
            [1] => 'Hello m'lady', 
            [2] => "null", 
            [3] => 'TE'ST'
        )

)

然而,更复杂的情况是:

$string = 'VAR_SELECTED, \'Hello "Father"\', "Hello \'Luke\'"';
preg_match_all('/(?:[^\']|\\\\.)+|(?:[^"]|\\\\.)+/', $string, $matches);
print_r($matches);  

输出:

Array
(
    [0] => Array
        (
            [0] => VAR_SELECTED, 
            [1] => 'Hello 
            [2] => "Father"
            [3] => ', 
            [4] => "Hello 
            [5] => 'Luke'
            [6] => "
        )

)

谁能帮我解决这个问题?多个正则表达式是前进的方向吗?

编辑也许用占位符替换字符串中的逗号然后用爆炸分解字符串会更容易?

编辑 2 只是想到了一个简单的不安全选项(我不会使用),但会产生 E_NOTICE 错误。

$string = 'return array(VAR_SELECTED, \'Hello , "Father"\', "Hello \'Luke\'4");';
$string = eval($string);
print_r($string);

【问题讨论】:

  • 您怎么知道“m'lady”中的撇号包含在您上面给出的示例中的引号内 - 如果字符串中有更多的单引号,这不会崩溃吗?跨度>
  • 这就是我想要解决的全部问题。

标签: php regex string preg-match-all


【解决方案1】:

试试这个:

/(?<=^|[\s,])(?:(['"]).*?\1|[^\s,'"]+)(?=[\s,]|$)/

或者,作为 PHP 单引号字符串文字:

'/(?<=^|[\s,])(?:([\'"]).*?\1|[^\s,\'"]+)(?=[\s,]|$)/'

那个正则表达式产生了预期的结果,但我认为你做错了。通常,如果带引号的字符串需要包含文字引号字符,则引号会被转义,使用反斜杠或另一个引号。你没有这样做,所以我不得不使用基于环视的脆弱黑客。您确定数据不应该是这样的吗?

$string = 'VAR_SELECTED, \'Hello m\\'lady\', "null"';

$string = 'VAR_SELECTED, \'Hello "Father"\', "Hello \\'Luke\\'"';

想一想,PHP 不是内置了对 CSV 数据的支持吗?

【讨论】:

  • 问题是他说逗号可以在字符串本身中,以及未转义的引号和引号的混合。我几乎认为他需要抓取字符串以找到不匹配的“开始”字符。但这对于 php 来说太糟糕了。
  • 谢谢,但我认为你的正则表达式有它。 PHP 确实有一个 CSV 解析器和一个 str 函数(php >= 5.3),但是对于这个问题,php 仍然无法正确解析数据,因为在同一个参数列表中,附件可以是“或”,我很傻知道但模板设计师很傻。@Caladain - 我认为这实际上解决了它。试试这个字符串与 preg_match。$string = 'VAR_SELECTED, \'Hello , "Father"\', "Hell,o \'Luke\'" , \',"\'';
  • 考虑字符串:$string = 'VAR_SELECTED, \'Hello, \' "Fa\'ther" \', "Hello, \'Luke, "my Son"\'"';不正确。我认为艾伦的建议在这里是正确的。环顾和回溯可能非常脆弱。具有统一格式和转义的数据使这成为一个更简单的问题,否则您永远无法保证您不会收到格式错误的字符串(有时是故意注入代码,有时是因为用户是敲击键盘的猴子并且不在乎正确转义的东西)
  • @Caladin - 您给出的字符串 while 不正确仍被分成其组件字符串。即使你考虑 $string = 'VAR_SELECTED, \'Hello, \' "Fa\'ther" \', "Hello, \'Luke, test';,由于每个单独匹配的后处理,我可以肯定地说没有脚本可以通过。所以现在,除非证明不是这样。
  • 我希望你能够改变设计,但我想不会。
【解决方案2】:

我会这样做:

将任务分解为您要执行的组件步骤:

1.) 用逗号分解字符串。

For 'VAR_SELECTED, \'Hello m\'lady\', "null"' this gives me
[0]=>"VAR_SELECTED"
[1]=>" \'Hello m\'lady\'"
[2]=>" "null""

For 'VAR_SELECTED, \'Hello "Father"\', "Hello \'Luke\'"' this gives me
[0]=>"VAR_SELECTED"
[1]=>" \'Hello "Father"\'"
[2]=>" "Hello \'Luke\'""

2.) 对所有三个运行 Trim 以消除任何空白

For 'VAR_SELECTED, \'Hello m\'lady\', "null"' this gives me
[0]=>"VAR_SELECTED"
[1]=>"\'Hello m\'lady\'"
[2]=>""null""

For 'VAR_SELECTED, \'Hello "Father"\', "Hello \'Luke\'"' this gives me
[0]=>"VAR_SELECTED"
[1]=>"\'Hello "Father"\'"
[2]=>""Hello \'Luke\'""

3.) 运行 str_replace(" \ "," ",$text) 以去除斜线。 (删除空格..添加只是为了便于阅读,所以应该是一个裸斜线和一个“空”字符串)

For 'VAR_SELECTED, \'Hello m\'lady\', "null"' this gives me
[0]=>"VAR_SELECTED"
[1]=>"'Hello m'lady'"
[2]=>""null""

For 'VAR_SELECTED, \'Hello "Father"\', "Hello \'Luke\'"' this gives me
[0]=>"VAR_SELECTED"
[1]=>"'Hello "Father"'"
[2]=>""Hello 'Luke'""

4.) 再次运行 trim,仅 trim($text, " ' " ") (删除空格..为可读性添加)

For 'VAR_SELECTED, \'Hello m\'lady\', "null"' this gives me
[0]=>"VAR_SELECTED"
[1]=>"Hello m'lady"
[2]=>"null"

For 'VAR_SELECTED, \'Hello "Father"\', "Hello \'Luke\'"' this gives me
[0]=>"VAR_SELECTED"
[1]=>"Hello "Father""
[2]=>"Hello 'Luke'"

我没有对此进行测试,但逻辑是合理的。测试 98% 的所有正则表达式(根据我的经验)的一种快速而肮脏的方法是使用 http://rubular.com/ 这是一个很棒的网站。通常,如果它开始在正则表达式上阻塞,这是我应该进一步分解问题的第一个迹象。 (这只是意见~穿上防火服~)

【讨论】:

  • 如果字符串本身不包含逗号,这将起作用,否则您也会得到损坏的字符串。
  • 实际上,您需要能够有一个模式或字符串来描述字符串中的每个“字段”。逗号,&,!...某事。否则任何方法都行不通..计算机太笨了。如果你在字符串中间给出你的分界符或模式,正则表达式或其他方法将在该模式上“分裂”..
  • 引号确实是这样吗?你的意思是一个不常见的字符串,比如 # 之类的
  • 并非如此。在您的示例中,您展示了字符串没有关闭所有引号的情况。 \'你好,女士\'。因此,如果我打破匹配的引号,该字符串将不起作用。是的,不常见的字符串是提供描述字符的标准(逗号、波浪号、像 00x0 这样永远不会出现的模式..等等)这是一个不平凡的问题 :-) 你必须有一些模式来“打破" 将字符串转换为可用字段。这就是为什么 PHP 字符串不能以 ' 开头并以 ".. 结尾的原因。解析器正在“匹配”单引号和双引号。
【解决方案3】:

您想在匹配字符串中使用back reference

preg_match_all('@([\'"]).*[^\\\\]\1@', $string, $matches);

这将从 " 或 ' 的第一个实例开始匹配,然后匹配以匹配 " 或 ' 结尾的最长字符串,没有转义。

Array (
[0] => Array
    (
        [0] => 'Hello m'lady', "null", 'TE'ST'
    )

[1] => Array
    (
        [0] => '
    )

【讨论】:

  • 嗯,需要的匹配项是 'Hello m'lady'、'null' 和 'TE'ST' 作为单独的字符串,而不是一个长字符串。
  • 哦,好吧。我误读了问题所在。这就是该死的 1 杯啤酒的障碍。
猜你喜欢
  • 2012-09-26
  • 2016-10-06
  • 2018-06-15
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
  • 2019-05-11
相关资源
最近更新 更多