正则表达式 - 忽略匹配中的某些字符串部分

10

这是我的字符串:

address='St Marks Church',notes='The North East\'s premier...'

我使用的正则表达式来使用match_all获取各个部分是:

The regex I'm using to grab the various parts using match_all is

'/(address|notes)='(.+?)'/i'

结果为:

地址 => 圣马克教堂
注释 => The North East\

如何使它忽略注释中的 \' 字符?


1
你是否只想在表达式中考虑字母数字字符? - user1639464
不要基本上任何在'和第二个'之间的内容,不包括'。我有点对正则表达式新手,所以可能前面的部分也错了吧? - Paul Phillips
3个回答

5

不确定您是使用heredoc还是双引号包装字符串,但以下是一种不太贪婪的方法:

$str4 = 'address="St Marks Church",notes="The North East\'s premier..."';
preg_match_all('~(address|notes)="([^"]*)"~i',$str4,$matches);
print_r($matches);

输出

Array
(
    [0] => Array
        (
            [0] => address="St Marks Church"
            [1] => notes="The North East's premier..."
        )

    [1] => Array
        (
            [0] => address
            [1] => notes
        )

    [2] => Array
        (
            [0] => St Marks Church
            [1] => The North East's premier...
        )

)

使用 preg_split 的另一种方法:

//split the string at the comma
//assumes no commas in text
$parts = preg_split('!,!', $string);
foreach($parts as $key=>$value){
    //split the values at the = sign
    $parts[$key]=preg_split('!=!',$value);
    foreach($parts[$key] as $k2=>$v2){
        //trim the quotes out and remove the slashes
        $parts[$key][$k2]=stripslashes(trim($v2,"'"));
    }
}

输出看起来像:
Array
(
    [0] => Array
        (
            [0] => address
            [1] => St Marks Church
        )

    [1] => Array
        (
            [0] => notes
            [1] => The North East's premier...
        )

)

超级缓慢的老派方法:

$len = strlen($string);
$key = "";
$value = "";
$store = array();
$pos = 0;
$mode = 'key';
while($pos < $len){
  switch($string[$pos]){
    case $string[$pos]==='=':
        $mode = 'value';
        break;
    case $string[$pos]===",":
        $store[$key]=trim($value,"'");
        $key=$value='';
        $mode = 'key';
        break;
    default:
        $$mode .= $string[$pos];
  }

  $pos++;
}
        $store[$key]=trim($value,"'");

你的第一个方法调整了输入字符串以适应该方法,这个方法应该被删除。第二个方法使用 preg_split(),而 explode() 是更合理的函数调用。此外,如果字符串中可能出现 \',那么可以假设 ,= 也可能出现。第三个方法我还没有测试过,但它要么有拼写错误,要么正在使用变量变量,应尽可能避免使用。 - mickmackusa
我取消了我的踩票,因为我欣赏你正在努力修复你的答案。不幸的是,我觉得我不得不重新踩票,因为这个答案提出了贫乏和/或不可靠的方法。 - mickmackusa
为不良数据存储方法让步从来都不是一个好主意。这个文本流应该以 JSON、XML 或者甚至 CSV 的形式进行存储,并且最好使用行业标准的处理方法进行处理。当然,我们非常欢迎您的意见。 - AbsoluteƵERØ

2
因为您发布了使用match_all,并且您个人资料中的顶级标签是phpwordpress,我认为可以合理地假设您正在使用php的preg_match_all()
以下模式将匹配构建所需关联数组的子字符串:
生成完整字符串匹配和1个捕获组的模式:
  1. /(address | notes)='\\ K(?:\\\'|[^'])* / (166步骤,演示链接
  2. /(address | notes)='\\ K。*?(? =(?<!\\)') / (218步骤,演示链接
生成2个捕获组的模式:
  1. /(address | notes)='((?:\\\'|[^'])*)/ (168步骤,演示链接
  2. /(address | notes)='(.*?(?<!\\))'/ (209步骤,演示链接
代码:(演示
$string = "address='St Marks Church',notes='The North East\'s premier...'";

preg_match_all(
    "/(address|notes)='\K(?:\\\'|[^'])*/",
    $string,
    $out
);
var_export(array_combine($out[1], $out[0]));

echo "\n---\n";

preg_match_all(
    "/(address|notes)='((?:\\\'|[^'])*)/",
    $string,
    $out,
    PREG_SET_ORDER
);
var_export(array_column($out, 2, 1));

输出:

array (
  'address' => 'St Marks Church',
  'notes' => 'The North East\\\'s premier...',
)
---
array (
  'address' => 'St Marks Church',
  'notes' => 'The North East\\\'s premier...',
)

模式1和3使用备选方案,允许非撇号字符或未经反斜杠前缀的撇号。

模式2和4(在使用php实现时需要额外的反斜杠演示)使用回顾环来确保由反斜杠前缀的撇号不会结束匹配。

一些注意事项:

  • 使用捕获组、备选方案和回顾环通常会降低模式效率。限制使用这些组件通常可以提高性能。使用贪婪量词的否定字符类通常可以提高性能。

  • 使用\K(重新启动完整字符串匹配)在试图减少捕获组并减小输出数组大小时非常有用。


@PaulPhillips 四年过去了,你可能不再是正则表达式的新手了。请查看本页面上的所有答案。遗憾的是,本页面上的其他答案是不准确/错误的,并且随着时间的推移已经获得了赞数(这意味着它们多年来一直在误导读者)。如果您对我的答案或其他答案为什么不正确有任何疑问,我将很乐意解释。 - mickmackusa
嘿,Mick,你是在挑衅每个人的过去回答还是只有我的? - AbsoluteƵERØ
我在另一个StackExchange网站上寻找答案时偶然发现了这个页面。我的行为没有任何恶意。如果我想要恶作剧,我会骂你或者干脆不留评论。我所做的是识别出一个包含3个错误答案(现在只有2个,因为anubhava删除了他的答案)的页面,合理地对误导性的错误答案进行了负面评价,留下了解释性评论(带有演示链接),编辑了问题,并提供了全面而周到的答案。我所做的应该被认为是“内容改进”。 - mickmackusa
我猜它以前是可以工作的(虽然我不确定如何),否则人们只是匆匆一瞥就认为它可以工作,尽管它被标记为答案,所以它可能帮助OP解决了他们的问题。无论如何。 - AbsoluteƵERØ
它从未按预期工作。原帖发布者盲目地相信了答案。多年来,这种盲目的信任像雪球一样越滚越大。 - mickmackusa

1
您需要匹配到一个没有反斜杠前缀的结束引号,如下所示:
(address|notes)='(.*?)[^\\]'

这个[^\\]强制要求在'字符之前的字符不能是反斜杠。

如果输入是:"address='.',notes='The North East\'s premier...'",那么这个会起作用吗? - anubhava
正如@anubhava所暗示的那样,这个答案是错误的,并且会扭曲预期的返回值。https://regex101.com/r/90fBSr/1(因误导而被downvote) - mickmackusa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接