2012-03-18 46 views
2

我需要用逗號分割一個字符串,該逗號不會出現在帶引號的子字符串中。我的做法是當用正則表達式替換時,Ruby gsub不遵守命名組

  • 與一些特殊的記號引用子內更換了逗號,
  • 斯普利特用逗號的字符串,然後
  • 用逗號替換標記的出現(在分割字符串) 。

我意識到可能有一個更簡單的方法來做到這一點,但現在我只是想知道爲什麼指定的組替換不能正常工作,如下所述。

我有引述子內標識逗號作爲命名捕獲commahere正則表達式:

COMMA_INSIDE_QUOTES_REGEX =/
    (?<quote>[\"\'])  # start by finding either single or double quote 
    (?<postquote>.*?)  # then lazy capture any other chars until... 
    (?<commahere>\,)  # ...we find the comma 
    (?<postcomma>.*?)  # then lazy capture any other chars until... 
    (\k<quote>)   # ...we find the matching single or double quote 
/x 

在下面的測試字符串,正則表達式匹配de,fjk,a,l而不是其他人,如我所料。

str = 'abc,"de,f",ghi,"jk,a,l"' 
COMMA_INSIDE_QUOTES_REGEX.match(str) 
#=> #<MatchData "\"de,f\"" quote:"\"" postquote:"de" commahere:"," postcomma:"f"> 

但是當我使用gsub來代替用特殊標記命名捕獲,整場比賽被替換,而不是命名組(加兩個逗號!):

COMMA_TOKEN = '<--COMMA-->' 
str.gsub(COMMA_INSIDE_QUOTES_REGEX,"\\k<commahere>#{COMMA_TOKEN}") 
#=> "abc,,<--COMMA-->,ghi,,<--COMMA-->" 
+3

CSV是一起工作令人驚訝的不愉快的格式。通過在標準庫中使用CSV解析器,可以避免不必要的痛苦和折磨。 – 2012-03-18 19:30:11

+0

同意。不要重蹈覆轍,CSV解析是一項解決和完成的任務。使用已被證實可行的事情。 – DGM 2012-03-18 19:52:08

+0

@ muistooshort你怎麼知道它的CSV?它可能是,也可能不是。 – sawa 2012-03-18 20:48:44

回答

0

那是怎麼gsub作品。 gsub用替換字符串替換整個匹配。否則,gsub將如何知道要替換整個匹配的哪個子字符串?那些信息在哪裏?

爲了排除子字符串被包含在被替換的部分中,您必須根據需要使用回溯,負向回溯,預測或負向預測。但是,回溯不允許長度可變的字符串,因此您可以使用回溯或向前看quotepostcomma,但必須在替換字符串中重現postquote部分。

你的正則表達式還有其他幾個錯誤的東西。像",,這樣的常數子串很容易被稱爲is。用quotecommahere這樣的名稱來捕獲它們是沒有意義的。此外,它看起來像你不知道如何在正則表達式中構造替換字符串。如果您想用別的東西替換它,則不應在替換字符串中使用\k<commahere>

+0

謝謝sawa。我捕獲的持續子串只是爲了幫助我自己跟蹤正則表達式正在做什麼。在調試過程中,至少對我來說,這會造成很大的不同。把它看作內聯文檔,一旦一切正常工作,就會被刪除。 – 2012-03-19 02:14:09

+0

我明白了。我明白。 – sawa 2012-03-19 02:16:25

3

你誤解了一些東西。

str.gsub(COMMA_INSIDE_QUOTES_REGEX,"\\k<commahere>#{COMMA_TOKEN}") 

表示:

  1. 嘗試將字符串str內的正則表達式匹配COMMA_INSIDE_QUOTES_REGEX
  2. 如果成功的話,更換是整個通過從<commahere>的內容和COMMA_TOKEN內容建立一個字符串匹配

這並不意味着「只用組件<commahere>替換它後面的任何組件。你的方法是錯誤的,你試圖做的事情不能像你試圖做的那樣完成。你的確應該接受mu的建議並使用CSV解析器。

如果你有興趣在什麼正則表達式將是一個能實際工作,那就這樣就可以了:

  1. 匹配一個逗號。
  2. 檢查這個逗號是否在一個字符串內。這可以通過計算逗號後面的引號數來完成。如果該數字很奇怪,則逗號在一個字符串內。
  3. 即使報價嵌入在字符串本身中,前面的技巧也能正常工作,因爲這些報價是通過加倍轉義的。

所以,這是你的正則表達式:

result = str.gsub(
    /,  # Match a comma 
    (?!  # only if it's not followed by 
    (?:  # the following group: 
     [^"]*" # any number of non-quote characters and a quote 
     [^"]*" # twice (so exactly two quotes are matched) 
    )*  # any number of times (including 0) 
    [^"]* # followed (if at all) by only non-quote characters 
    \Z  # until the end of the string. 
    )   # End of lookahead 
    /x, '<--COMMA-->') 
+0

謝謝大家。你是對的。我誤解了gsub上的Ruby文檔:如果replacement是一個String,它將替換匹配的文本。它可能包含對\\ d格式的捕獲組的反向引用,其中d是一個組號,我解析說,這意味着捕獲組可以用作替代目標。而且,CSV提示讓我做了一個手掌對額頭的感覺。 ;-) – 2012-03-19 02:09:13