2012-02-11 203 views
0

之间请考虑以下字符串需要正则表达式来不顾一切就是括号

$text = "Dat foo 13.45 and $600 bar {baz:70} and {8}"; 

我需要标注的所有号码在$文本,除非他们是花括号之间进行。我现在有这样的:

preg_replace("/(?<!{)([0-9]+(?:\.[0-9]+)?)(?!})/","{NUMBER:$0}",$text); 

,其输出:

Dat foo {NUMBER:13.45} and $ {NUMBER:600} bar {baz: {NUMBER:7} 0} and {8} 

然而,所期望的输出是:

Dat foo {NUMBER:13.45} and ${NUMBER:600} bar {baz:70} and {8} 

其中之间的数{和}被忽略,即使它们被包围由alfanumerical(或其他)字符。换句话说 - 我该如何调整正则表达式才能完全忽略大括号之间的任何内容?

回答

3
(?<!{)(?>[0-9]+(?:\.[0-9]+)?)(?!}) 

Atomic grouping. 也许回顾后是不是真的需要。

+0

谢谢!奇迹般有效! – Pr0no 2012-02-11 01:05:15

+0

-1,错误的,例如:'1 {a2:b} c' – Qtax 2012-02-11 01:09:43

+0

在一般情况下(嵌套括号)你不能这样做,即使你的括号只能是一个深度,你仍然需要任意长度的lookbehinds,php似乎没有。 – user1096188 2012-02-11 01:49:10

2

你可以以这种方式使用/e

preg_replace("/(\\d+(?:\\.\\d+)?)|{[^}]+}/e", '"$1"?"{NUMBER:$1}":"$0"', $text); 

结果是:

Dat foo {NUMBER:13.45} and ${NUMBER:600} bar {baz:70} and {8} 

像这样的另类黑客会如果{groups}总是平衡的,有没有松动{}工作任何地方:

preg_replace("/\\d+(?:\\.\\d+)?(?![^{}]*})/", '{NUMBER:$0}', $text); 

但第一个解决方案是更好的伊莫。

0

你可以实现一个简单的解析器来代替:

<?php 
function parse($str){ 
    $res = ""; 
    $tmp_res = ""; 
    $ignore = false; 
    for ($i = 0; $i < strlen($str); $i++) { 
     $char = $str[$i]; 
     if ($char === "{"){ 
      while ($char != "}"){ 
       $res = $res . $char; 
       $i++; 
       $char = $str[$i]; 
      } 
     } 
     if(is_numeric($char)){ 
      $res = $res . "{NUMBER:$char"; 
      $i++; 
      $char = $str[$i]; 
      while (is_numeric($char) || $char == '.'){ 
       $res = $res . $char; 
       $i++; 
       $char = $str[$i]; 
      } 
      $res = $res . "}" . $char; // add the "}" 
     } 
     else{ 
      $res = $res . $char; 
     } 
    } 
    return $res; 
} 

$text = parse("Dat foo 13.45 and $600 bar {baz:70} and {8}"); 
echo $text; 

?> 

但我必须承认,使用正则表达式是更优雅!