2008-09-26 77 views
7

我需要替換字符(比如說)x與字符(比如說)P在一個字符串中,但僅當它包含在引用子字符串中時。 的一個例子使之更清晰:Can Regex可以用於這個特定的字符串操作嗎?

axbx'cxdxe'fxgh'ixj'k -> axbx'cPdPe'fxgh'iPj'k 

假設,爲了簡單起見,報價總是成對出現。

最顯而易見的方法是隻一次處理(一個簡單的狀態機的方法)將字符串一個字符;然而,我想知道是否可以使用正則表達式一次完成所有處理。

我的目標語言是C#,但我想我的問題是指具有內置任何語言或正則表達式庫支持。

+0

刪除 '規則表達式' 標籤 – 2008-09-26 16:10:59

回答

8

我可以使用Python做到這一點:

>>> import re 
>>> re.sub(r"x(?=[^']*'([^']|'[^']*')*$)", "P", "axbx'cxdxe'fxgh'ixj'k") 
"axbx'cPdPe'fxgh'iPj'k" 

這樣做的是使用非獲取匹配(= ...?)檢查字符x在引用字符串內。它會查找一些直到下一個引號的非引號字符,然後查找單個字符或帶引號的字符組的序列,直到字符串結束。

這依賴於你的假設,即引號總是平衡的。這也不是很有效。

+0

也考慮到它的應用re.sub在替換 'X' 由 'P'( )函數遍歷字符串。正則表達式本身只匹配引號內的第一個x。 – 2008-09-26 10:33:31

+0

我無法想象如何使用像re.sub()這樣的東西來解決這個問題。畢竟,正則表達式本身只是匹配,而原始問題是關於替換的問題。 – 2008-09-26 10:36:52

1

不是用普通的正則表達式。正則表達式沒有「記憶」,因此它們無法區分是「內部」還是「外部」引號。

您使用gema這將是簡單明瞭需要更強大的東西,例如:

'<repl>'=$0 
repl:x=P 
0

不好意思去打破你的希望,但你需要一個下推自動機來做到這一點。這裏有更多的信息: Pushdown Automaton

簡而言之,規則表達式是有限狀態機只能讀取並且沒有內存,而下推自動機具有堆棧和操作能力。

編輯:拼寫...

1

約平衡文本類似的討論取代:Can regular expressions be used to match nested patterns?

雖然你可以試試這個在Vim的,但它運作良好,只有當字符串是在一行上,而且也只有一對的的。

:%s:\('[^']*\)x\([^']*'\):\1P\2:gci 

如果還有一對或甚至不平衡',那麼它可能會失敗。那樣,我在ex命令中包含了c又名確認標誌。

同樣可以使用sed來完成,沒有互動 - 或awk這樣你就可以添加一些互動。

一個可行的辦法是打破對'當時的你可以用vim的解決方案做線。

9

我將Greg Hewgill的python代碼轉換爲C#,它工作正常!

[Test] 
public void ReplaceTextInQuotes() 
{ 
    Assert.AreEqual("axbx'cPdPe'fxgh'iPj'k", 
    Regex.Replace("axbx'cxdxe'fxgh'ixj'k", 
     @"x(?=[^']*'([^']|'[^']*')*$)", "P")); 
} 

該測試通過。

1
Pattern:  (?s)\G((?:^[^']*'|(?<=.))(?:'[^']*'|[^'x]+)*+)x 
Replacement: \1P 
  1. \G —錨每個匹配在先前一個或字符串的開始的端部。
  2. (?:^[^']*'|(?<=.)) —如果它在字符串的開頭,則匹配到第一個引號。
  3. (?:'[^']*'|[^'x]+)*+ —匹配任何未加引號的字符塊或任何(非引號)字符到'x'。

一次掃過槽源字符串,除了單個字符後視。

2

訣竅是使用非捕獲組字符串的以下匹配(字符X)我們正在搜索的部分相匹配。 試圖匹配高達x的字符串只會查找第一個或最後一個發生,具體取決於是否使用非貪婪量詞。 這裏是格雷格的想法轉置到Tcl的評論。

 
set strIn {axbx'cxdxe'fxgh'ixj'k} 
set regex {(?x)      # enable expanded syntax 
            # - allows comments, ignores whitespace 
      x      # the actual match 
      (?=      # non-matching group 
       [^']*'    # match to end of current quoted substring 
            ## 
            ## assuming quotes are in pairs, 
            ## make sure we actually were 
            ## inside a quoted substring 
            ## by making sure the rest of the string 
            ## is what we expect it to be 
            ## 
       (
        [^']*   # match any non-quoted substring 
        |    # ...or... 
        '[^']*'   # any quoted substring, including the quotes 
       )*     # any number of times 
       $     # until we run out of string :) 
      )      # end of non-matching group 
} 

#the same regular expression without the comments 
set regexCondensed {(?x)x(?=[^']*'([^']|'[^']*')*$)} 

set replRegex {P} 
set nMatches [regsub -all -- $regex $strIn $replRegex strOut] 
puts "$nMatches replacements. " 
if {$nMatches > 0} { 
    puts "Original: |$strIn|" 
    puts "Result: |$strOut|" 
} 
exit 

此打印:

3 replacements. 
Original: |axbx'cxdxe'fxgh'ixj'k| 
Result: |axbx'cPdPe'fxgh'iPj'k| 
2
#!/usr/bin/perl -w 

use strict; 

# Break up the string. 
# The spliting uses quotes 
# as the delimiter. 
# Put every broken substring 
# into the @fields array. 

my @fields; 
while (<>) { 
    @fields = split /'/, $_; 
} 

# For every substring indexed with an odd 
# number, search for x and replace it 
# with P. 

my $count; 
my $end = $#fields; 
for ($count=0; $count < $end; $count++) { 
    if ($count % 2 == 1) { 
     $fields[$count] =~ s/a/P/g; 
    }  
} 

這是不是塊做的工作?

2

允許非配對引號的更一般(更簡單)的解決方案。

  1. 查找引用字符串
  2. 字符串

    #!/usr/bin/env python 
    import re 
    
    text = "axbx'cxdxe'fxgh'ixj'k" 
    
    s = re.sub("'.*?'", lambda m: re.sub("x", "P", m.group(0)), text) 
    
    print s == "axbx'cPdPe'fxgh'iPj'k", s 
    # -> True axbx'cPdPe'fxgh'iPj'k 
    
相關問題