2013-03-19 67 views
1

我感到羞愧,但我仍然不清楚正則表達式的一些方面。 我需要解析文本文件,其中包含一些@"I'm a string"格式的字符串文字。 我已經組成了簡單的模式/@"([^"]*)"/si。它工作完美,preg_match_all返回一個集合。但是,如果字符串文字包含像@"I'm plain string. I'm \"qouted\" string "這樣的轉義引號,顯然它不能正常工作。將不勝感激任何線索。Assism with regexp

+0

其實我想你的轉義字符串和模式似乎工作 – aleation 2013-03-19 12:15:02

+0

是,該模式起作用,但由佔位符([^「] *)佔用的值不如預期的那樣 – heximal 2013-03-19 12:18:55

+0

'preg_match_all('/@"(.*)"$/ si',$ text,$ match);'。?? – MatRt 2013-03-19 12:20:28

回答

2

這是一個用例Freidl的經典作品「展開循環」:(編輯固定捕獲分組)

/"((?:[^"\\]|\\.)*)"/ 

這將匹配引用字符串,以反斜槓轉義引號考慮。

你可以使用匹配字段(包括@)的完整的regex是:

/@"((?:[^"\\]|\\.)*)"/ 

但要小心!我經常看到有人抱怨說這種模式在PHP中不起作用,這是因爲在字符串中使用了反斜槓的略微融化的性質。

上述模式中的反斜槓代表文字需要傳遞到PCRE的反斜槓。這意味着,他們需要在PHP字符串中使用時,他們是兩次轉義:

$expr = '/@"((?:[^"\\\\]|\\\\.)*)"/'; 

preg_match_all($expr, $subject, $matches); 

print_r($matches[1]); // this will show the content of all the matched fields 

See it working

它是如何工作的?

...我聽到你問。那麼,讓我們看看我能否以一種真正有意義的方式解釋這一點。讓我們使x模式,這樣我們就可以空出來了一下:

/ 
    @    # literal @ 
    "    # literal " 
    (   # start capture group, we want everything between the quotes 
     (?:  # start non-capturing group (a group we can safely repeat) 
     [^"\\] # match any character that's not a " or a \ 
     |  # ...or... 
     \\.  # a literal \ followed by any character 
    )*  # close non-capturing group and allow zero or more occurrences 
    )   # close the capture group 
    "    # literal " 
/x 

這真的很重要的點是這些:

  • [^"\\]|\\. - 意味着每一個反斜槓是「平衡」 - 每一個反斜槓必須轉義字符,並且不會有一次以上的角色被考慮。
  • *包裹在上面的重複組意味着上述模式可以發生無限次數,並且允許空字符串(如果您不想允許空字符串,請將*更改爲+)。這是「展開循環」的「循環」部分。

但輸出字符串仍然包含逃脫引號的反斜線?

確實如此,這只是一個匹配程序,它不會修改匹配。但是因爲結果是字符串的內容,所以簡單的str_replace('\\"', '"', $result)將是安全的並且產生正確的結果。

但是,做這樣的事情的時候,我經常發現我要處理其他轉義序列,以及 - 在這種情況下,我通常會做這樣的事情的結果:

preg_replace_callback('/\\./', function($match) { 
    switch ($match[0][1]) { // inspect the escaped character 
     case 'r': 
      return "\r"; 

     case 'n': 
      return "\n"; 

     case 't': 
      return "\t"; 

     case '\\': 
      return '\\'; 

     case '"': 
      return '"'; 

     default: // if it's not a valid escape sequence, treat the \ as literal 
      return $match[0]; 
    } 
}, $result); 

這使得類似的行爲轉換爲PHP中的雙引號字符串,其中\t被選項卡替換,\n被替換爲換行符等。

如果我想允許單引號字符串呢?

這已經給我很長時間了。我一直有這樣一種微妙的感覺,認爲這可以通過反向引用更有效地處理,但許多嘗試未能產生任何可行的結果。

我這樣做:

/(?:"((?:[^"\\]|\\.)*)")|(?:'((?:[^'\\]|\\.)*)')/ 

正如你所看到的,這基本上是採用基本相同的圖案兩次,OR關係。該字符串的提取在PHP端略微複雜,以及:

$expr = '/(?:"((?:[^"\\\\]|\\\\.)*)")|(?:\'((?:[^\'\\\\]|\\\\.)*)\')/'; 

preg_match_all($expr, $subject, $matches); 

$result = array(); 
for ($i = 0; isset($matches[0][$i]); $i++) { 
    if ($matches[1][$i] !== '') { 
     $result[] = $matches[1][$i]; 
    } else { 
     $result[] = $matches[2][$i]; 
    } 
} 

print_r($result); 
+0

Where '\\。'會傾斜像\ t,\'\ n'等任何奇怪的斜線轉義+1 – 2013-03-19 12:23:09

+0

@Allendar這樣做的好處是,它只會在非轉義的雙引號,任何其他反斜槓組合逃生將保持不變。我會盡力以可理解的方式分解它是如何工作的。 – DaveRandom 2013-03-19 12:29:20

+0

謝謝,你讓我的一天,戴夫!非捕獲組正是我正在尋找的,這是錯過了我的正則表達式知識的一部分 – heximal 2013-03-19 13:57:26

0

您需要使用負回顧後 - 直到你找到一個報價沒有反斜槓匹配的一切。這是Java:

public static void main(String[] args) { 
    final String[] strings = new String[]{"@\"I'm a string\"", "@\"I'm plain string. I'm \\\"qouted\\\" \""}; 

    final Pattern p = Pattern.compile("@\"(.*)\"(?<!\\\\)"); 
    System.out.println(p.pattern()); 

    for (final String string : strings) { 
     final Matcher matcher = p.matcher(string); 
     while (matcher.find()) { 
      System.out.println(matcher.group(1)); 
     } 
    } 
} 

輸出:

I'm a string 
I'm plain string. I'm \"qouted\" 

模式(沒有所有的Java逃逸)是:@"(.*)"(?<!\\)

+0

是不是要求PHP的問題? – 2013-03-19 12:23:41

+0

正則表達式是正確的嗎?正則表達式模式的作品,OP只需要轉義任何需要在PHP中轉義的需求。 – 2013-03-19 12:24:55

+1

PHP Regex實際上有不同的規則,需要額外的轉義引號。除此之外,它可能會讓提問人員疑惑他/她是否缺乏JAVA的知識,即使這對我們大多數人來說也是有意義的;通常不會假設它是如此。你的正則表達式本身是正確的:) – 2013-03-19 12:26:50