2010-09-01 18 views
-4
$a = "<no> 3232 </no> " 

$a =~ s/<no>(.*)</no>/000/gi ; 

我期待$a變成"<no> 000 </no> ",但它不起作用。如何更換字符串的中間?

+5

這是什麼意思,「它不工作」?你得到什麼錯誤? – 2010-09-01 15:54:47

+0

它將所有字符串替換爲000 – Tree 2010-09-01 16:48:46

+12

這是一個隱形「如何修改XML文檔中的值」問題? – 2010-09-01 18:18:18

回答

9

你需要look-around assertions

$a =~ s|(?<=<no>).*(?= </no>)|000|gi; 
# $a is now "<no> 000 </no> " 

你有沒有考慮閱讀一兩本Perl書?如果你必須通過Stack Overflow來解答那些可以通過閱讀罰款文檔很容易回答的問題,那麼你的學習效率就不高。

+1

使用貪婪匹配('。*'而不是'。*?')在存在多個或嵌套標記時幾乎肯定會產生不希望的行爲(自從指定了'g'標誌後,您似乎期待多個標記)。使用惰性匹配('。*?')會在嵌套標籤的存在下產生不希望的行爲。至少限制損害:'s/ [^ <]*<\/no>//g'或's /(?<=)[\ s \ d] *(?= <\/no>)/ 000/g' – vladr 2010-09-10 02:31:09

3

首先,/ in被解釋爲您的模式的結束,這是導致語法錯誤。爲您的替代運算符選擇一個不同的分隔符:

s|<no>.*</no>|000|gi; 

但是,然後您有一組捕獲括號,並且您沒有使用它們捕獲的內容。這讓我想,甚至修復語法也不會給你想要的行爲。你不想更換標籤,這樣你就可以添加這些到更換:

s|<no>.*</no>|<no>000</no>|gi; 

還是不能代替他們在所有所以他們不匹配的文本的一部分使用lookarounds:

s|(?<=<no>).*(?=</no>)|000|gi; 

但是,鑑於「它不工作」不是一個很好的問題描述,我不知道你期望看到什麼。

+1

該解決方案還刪除了''和''標籤。 – mob 2010-09-01 17:54:20

+1

當然可以。但這不是一個解決方案。它重新實現了原始海報的內容,但沒有語法錯誤。那麼,也許我們可以開始討論他真正需要的東西了:-) – 2010-09-01 18:24:18

+1

現在davorg修復了這個問題:)我認爲OP可能會爲我們做更復雜的事情並且簡化它。 – 2010-09-01 18:53:01

4

如果你只是想替換標籤之間的文字,那麼你可能想看看lookahead and lookbehind assertions。而你需要可以使用正則表達式分隔符以外的「/」或難逃「/」中的正則表達式:

$a = "<no> 3232 </no> "; 
$a =~ s#(?<=<no>).*?(?=</no>)# 000 #gi; 
print "$a\n"; 
5

你可以放棄花哨的超前或環視斷言,並拿出一個稍長的正則表達式:

$str =~ s|<no>.*?</no>|<no>000</no>|gi; 

這可能是更容易一些閱讀,但它在你的小幅反直覺將<no>whatever</no>替換爲<no>000</no>,即您不只是替換<no></no>之間的內容,而是將整個字符串替換爲另一個字符串,該字符串恰好有<no></no>

1

首先,關閉中的/被視爲正則表達式的結束引號。無論是反斜線它:

$a =~ s/<no>(.*)<\/no>/000/gi; 

或使用不同的字符/在你的正則表達式:

$a =~ s~<no>(.*)</no>~000~gi; 

其次,我猜你試圖解析這個XML文檔並更改數據。我也猜測你的文檔中有很多<no> ... </no>部分。你給出的正則表達式的問題是(.*)將盡可能地與匹配,即,即之間的所有內容<no>最後</no>在您的文檔中,包括之間的任何其他標籤。它取代<no></no>

您可以使用非貪婪匹配,即儘可能少地匹配。你可以在後打上問號*像這樣:

$a =~ s~<no>(.*?)</no>~000~gi; 

由於這還是替換<no> ... </no>,你可能會想要把那些回:

$a =~ s~<no>(.*?)</no>~<no>000</no>~gi; 

在這種情況下你的<no>是一個正則表達式,你不能把它放到你的替換字符串中。您可以使用lookarounds別人的建議,或者只是捕捉到它,並把它放回使用$ 1 .. $ 9,像這樣:

$a =~ s~(<no>)(.*?)(</no>)~$1000$3~gi; 

爲什麼$ 3·因爲$ 2是您使用(.*?)拍攝的任何內容。當然,因爲你不真正關心你已經捕捉到了什麼,你可以這樣做:

$a =~ s~(<no>).*?(</no>)~$1000$2~gi; 

這大概是一樣有效,你要得到這個問題。

另一方面,嘗試使用正則表達式解析XML通常是一個糟糕的主意,因爲XML對於正則表達式來說分析太多了。我非常喜歡XML::LibXML用於處理XML文檔,但實現起來並不簡單。但是,如果您對XML的精確格式充滿信心(或者實際上它不是XML,但看起來有點像),那麼正則表達式就可以作爲本地黑客使用。

這些都包含在perlre聯機幫助頁面中,如果您要使用Perl正則表達式執行任何操作,即使遠程不重要,也是必讀內容。

$ perldoc perlre 

希望所有的例子有助於澄清一些事情。

1

只是爲了儘可能簡單,你有一些問題,所以讓我們先消除明顯的問題。

首先,您不能在字符串中使用斜線字符(「/」),因爲它對於每個字符具有特殊意義;例如「/n」表示打印一個新行,斜槓也用於分隔正則表達式的一部分。當你想使用斜線作爲文字時,解決方法是用反斜槓轉義斜線來告訴perl你真的想要斜線字符不是特別的。所以,你的原代碼,將得到更好的這樣寫的:

$a = "<no> 3232 <\/no> "; 
$a =~ s/<no>(.*)<\/no>/000/gi; 

現在的perl將解釋<\/no></no>

其次,您正則表達式是錯誤的。 s ///正則表達式指示perl用第二部分中的模式替換/重新格式化第一部分中的模式。您的指令會告訴perl用「000」替換前兩個斜線之間的所有內容,並將其分配給變量$ a。

您在正則表達式中使用的方括號允許您將表達式分解成sminner程序段並重新排列,但是您尚未使用它們,但是您處於正確的軌道上。要在要保留的第一組斜線中重新使用表達式的部分,請在其周圍放置括號。在表達式的第二部分中,您可以使用$ 1,$ 2等引用那些「塊」來引用每組括號內的內容。

牢記這一點你可能會拿出somethign像:

$a = "<no> 3232 <\/no> "; 
$a =~ s/(<no>).*(<\/no>)/$1000$2/gi; 

這是接近 - 如上述建議 - 但測試將揭示它仍然是不完全正確;這次你會得到的輸出更神祕,是</no>。這是因爲perl將字符串解釋爲$ 1000,其次是$ 2,而$ 1000不會引用任何內容。在$ 1之後放置一個空間或其他東西可以解決問題。 (有可能更正確地終止$ 1,但在這裏我要承認,我不知道它的一些方法。)

下面的表達式工作,但第一個讓你後,你會得到一個空間出認沽將<no> 000</no>

$a = "<no> 3232 <\/no> "; 
$a =~ s/(<no>).*(<\/no>)/$1 000$2/gi; 

我更傾向於將代替字符串「000」的,爲此,使用可變我的代碼可能會是這個樣子:

$a = "<no> 3232 <\/no> "; 
$b = "000"; 
$a =~ s/(<no>).*?(<\/no>)/$1$b$2/gi; 

使用VAR可以使事情在我看來更清楚一些(儘管它們可以更好地命名!),並且還允許替換文本(「000」),而不用亂搞正則表達式就可以輕鬆更改。這個?在正則表達式中是爲了確保正則表達式不會在字符串中存在多個無元素集時變得「貪婪 - 這會導致。*在遇到匹配模式時立即進行匹配,在這種情況下「」

相關問題