【问题标题】:PHP preg_split() patternPHP preg_split() 模式
【发布时间】:2018-07-29 22:16:22
【问题描述】:

我需要使用 preg_split() 查找 PCRE 模式的帮助。

我使用下面的正则表达式模式根据字符串的起始 3 个字符代码和分号来拆分字符串。该模式在 Javascript 中运行良好,但现在我需要在 PHP 中使用该模式。我尝试了 preg_split() 但只是退回垃圾。

// 每组都以三个字母代码开头,三个段用分号隔开。字符串不会以分号结尾。

// Pseudocode    
string_to_split = "AAA;RED;111;BBB;BLUE;22;CCC;GREEN;33;DDD;WHITE;44"

// This works in JS  
// https://regex101.com  
$pattern = "/[AAA|BBB|CCC|DDD][^;]*;[^;]*[;][^;]*/gi";

Match 1  
Full match  0-11    `AAA;RED;111`  
Match 2  
Full match  12-23   `BBB;BLUE;22`  
Match 3  
Full match  24-36   `CCC;GREEN;33`  
Match 4  
Full match  37-49   `DDD;WHITE;44`  

$pattern = "/[AAA|BBB|CCC|DDD][^;]*;[^;]*[;][^;]*/";  
$split = preg_split($pattern, $string_to_split);

returns

array(5)  
    0:""  
    1:";"  
    2:";"  
    3:";"  
    4:""  

【问题讨论】:

  • [AAA|BBB|CCC|DDD] 看起来不像在 JS 中正确运行。你的意思是(AAA|BBB|CCC|DDD)
  • 预期结果是什么?
  • [AAA|BBB|CCC|DDD][ABCD|] 相同,它的意思是“来自字符类内部的任何字符(即在[] 之间)”。你想要的是(AAA|BBB|CCC|DDD),这意味着AAABBBCCC DDD
  • @axiac,您可以使用正则表达式在 JavaScript 中拆分字符串。
  • @fubar 确实,String.prototype.split() 接受 RegExp 作为参数。但是,需求的描述要求匹配,而不是拆分。

标签: php pcre preg-split


【解决方案1】:

根据您在某些 cmets 中对答案的附加信息,我更新了我的答案以非常具体地针对您的源格式。

你可能想要这样的东西:

$subject = "AAA;RED;111;AAA;Oh my dog;12.34;AAA;Oh Long John;.4556;BBB;Oh Long Johnson;1.2323;BBB;Oh Don Piano;.33;CCC;Why I eyes ya;1.445;CCC;All the live long day;2.3343;DDD;Faith Hilling;.89";

$pattern = '/(?<=;|^)(AAA|BBB|CCC|DDD);([^;]*);((?:\d*\.)?\d+)(?=;|$)/';
preg_match_all($pattern, $subject,$matches);
var_dump($matches);

给你

array (size=4)
  0 =>
    array (size=8)
      0 => string 'AAA;RED;111' (length=11)
      1 => string 'AAA;Oh my dog;12.34' (length=19)
      2 => string 'AAA;Oh Long John;.4556' (length=22)
      3 => string 'BBB;Oh Long Johnson;1.2323' (length=26)
      4 => string 'BBB;Oh Don Piano;.33' (length=20)
      5 => string 'CCC;Why I eyes ya;1.445' (length=23)
      6 => string 'CCC;All the live long day;2.3343' (length=32)
      7 => string 'DDD;Faith Hilling;.89' (length=21)
  1 =>
    array (size=8)
      0 => string 'AAA' (length=3)
      1 => string 'AAA' (length=3)
      2 => string 'AAA' (length=3)
      3 => string 'BBB' (length=3)
      4 => string 'BBB' (length=3)
      5 => string 'CCC' (length=3)
      6 => string 'CCC' (length=3)
      7 => string 'DDD' (length=3)
  2 =>
    array (size=8)
      0 => string 'RED' (length=3)
      1 => string 'Oh my dog' (length=9)
      2 => string 'Oh Long John' (length=12)
      3 => string 'Oh Long Johnson' (length=15)
      4 => string 'Oh Don Piano' (length=12)
      5 => string 'Why I eyes ya' (length=13)
      6 => string 'All the live long day' (length=21)
      7 => string 'Faith Hilling' (length=13)
  3 =>
    array (size=8)
      0 => string '111' (length=3)
      1 => string '12.34' (length=5)
      2 => string '.4556' (length=5)
      3 => string '1.2323' (length=6)
      4 => string '.33' (length=3)
      5 => string '1.445' (length=5)
      6 => string '2.3343' (length=6)
      7 => string '.89' (length=3)

开始标记应该出现在字符串的开头或分号之后,所以我们向后看,寻找开始或分号:

(?<=;|^)

我们寻找 AAA、BBB、CCC 或 DDD 的替代方案并捕获它:

(AAA|BBB|CCC|DDD)

在分号之后,我们查找除分号之外的任何字符。量词* 表示0 或更多时间。如果您想要至少 1 个,请使用 +

;([^;]*)

在下一个分号之后寻找一个数字。必须拆分此任务以适应有效格式:我们首先查找 0 个或多个数字后跟一个点:

(?:\d*\.)?

其中(?:) 表示非捕获组。

我们在后面寻找至少一位数字:\d+

我们想在搜索的分号后面使用括号来捕获数字的两个部分:

;((?:\d*\.)?\d+)

这匹配“1234”、“.1234”、“1.234”、“12.34”、“123.4”但“1234.”、“1.2.3”

最后,我们希望它立即出现在分号或字符串结尾之前。因此我们进行了前瞻:

(?=;|$)

Lookaheads 和lookbehinds 不是后面或之前捕获结果的一部分。

【讨论】:

  • 我试过这种模式 "/[AAA|BBB|CCC|DDD]{3}(?:;[^;]*){2}/" 但没用。跨度>
  • 这不是我给的表达方式。我已经更新了我的答案,使其更具体地针对您在其他 cmets 中另外给出的源格式。
【解决方案2】:

我稍微修改了你的模式,并在preg_split 中添加了几个标志。

PREG_SPLIT_NO_EMPTY 标志将从结果中排除空匹配项,PREG_SPLIT_DELIM_CAPTURE 将在结果中包含捕获的值。

$split = preg_split('/([abcd]{3};[^;]+;\d+);?/i', $string, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);

结果:

Array
(
    [0] => AAA;RED;111
    [1] => BBB;BLUE;22
    [2] => CCC;GREEN;33
    [3] => DDD;WHITE;44
)

另外,更合适的是,您可以将preg_match_all 与以下模式一起使用。

preg_match_all('/([abcd]{3};[^;]+;\d+);?/i', $string, $matches);

print_r($matches[0]);

结果:

Array
(
    [0] => AAA;RED;111
    [1] => BBB;BLUE;22
    [2] => CCC;GREEN;33
    [3] => DDD;WHITE;44
)

【讨论】:

  • 你明白了。谢谢。我只需要解决小数点。 “AAA;RED;111;AAA;哦我的狗;2.34;AAA;哦龙约翰;.4556;BBB;哦龙约翰逊;1.2323;BBB;哦唐钢琴;.33;CCC;为什么我盯着你;1.445; CCC;万岁;2.3343;DDD;Faith Hilling;.89"; preg_split('/([AAA|BBB|CCC|DDD]{3};[^;]+;[0-9]\.[0-9]\d+);?/i', $string, -1 , PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE) 0:"AAA;RED;111;" 1:“AAA;哦,我的狗;2.34” 2:“AAA;哦,长约翰;.4556;” 3:“BBB;Oh Long Johnson;1.2323” 4:“BBB;Oh Don Piano;.33;” 5:"CCC;为什么我盯着你看;1.445" 6:"CCC;All the live long day;2.3343" 7:"DDD;Faith Hilling;.89"
  • 那么你也只需要匹配十进制值吗?还是有其他问题?
  • [a|b|c|d]{3} 不合适。它等同于[abc|],事实证明,它匹配例如"ab|""a|a"。目前尚不完全清楚提问者是否只想要“AAA”或“ABC”的三组。这些示例仅建议“AAA”、“BBB”。然而,| 在这个地方是错误的。
  • @Quasimodo'sclone - 拨打| 很好。我已经更新了我的答案。
【解决方案3】:

您不想拆分字符串而是匹配元素,请使用 preg_match_all:

$str = "AAA;RED;111;AAA;Oh my dog;2.34;AAA;Oh Long John;.4556;BBB;Oh Long Johnson;1.2323;BBB;Oh Don Piano;.33;CCC;Why I eyes ya;1.445;CCC;All the live long day;2.3343;DDD;Faith Hilling;.89";
$res = preg_match_all('/(?:AAA|BBB|CCC|DDD);[^;]*;[^;]*;?/', $str, $m);
print_r($m[0]);

输出:

Array
(
    [0] => AAA;RED;111;
    [1] => AAA;Oh my dog;2.34;
    [2] => AAA;Oh Long John;.4556;
    [3] => BBB;Oh Long Johnson;1.2323;
    [4] => BBB;Oh Don Piano;.33;
    [5] => CCC;Why I eyes ya;1.445;
    [6] => CCC;All the live long day;2.3343;
    [7] => DDD;Faith Hilling;.89
)

说明:

/                       : regex delimiter
  (?:AAA|BBB|CCC|DDD)   : non capture group AAA or BBB or CCC or DDD
  ;                     : a semicolon
  [^;]*                 : 0 or more any character that is not a semicolon
  ;                     : a semicolon
  [^;]*                 : 0 or more any character that is not a semicolon
  ;?                    : optional semicolon
/                       : regex delimiter

【讨论】:

  • 谢谢。细分有帮助。
猜你喜欢
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-23
  • 1970-01-01
  • 1970-01-01
  • 2016-08-12
相关资源
最近更新 更多