2008-09-17 180 views
4

我有一個字符串,以//#...開頭,轉到換行符。我已經找出了這個..#([^\n]*)的正則表達式。Perl正則表達式匹配和刪除

我的問題是你如何從一個文件,如果下列條件匹配

回答

27

你的正則表達式被嚴重選擇上幾點:

  1. 代替匹配兩個特別是,你使用..來匹配兩個字符,它們可以是任何東西,大概是因爲當你也將它們用作分隔符時,你不知道如何匹配斜槓。 (實際上,點匹配幾乎任何東西,因爲我們將看到在#3)。

    在斜槓分隔的正則表達式文字,//,你可以簡單地通過保護他們用反斜槓,如匹配斜槓。 /\/\//。但是,更好的變體是使用更長的正則表達式,m//,您可以在其中選擇分隔符,例如。 m!!。由於您使用非斜線劃定界限,因此您可以在不脫離它們的情況下編寫它們:m!//!。請參閱perldoc perlop

  2. 它不固定在字符串的開頭,所以它可以匹配任何地方。在前面使用^開始字符串斷言。

  3. 你寫了[^\n]以匹配「除了換行符之外的任何字符」,當有一種更簡單的方法來寫時,這只是.通配符。它的確如此 - 匹配除了換行符之外的任何字符。

  4. 您正在使用圓括號對匹配的一部分進行分組,但該組既不是量化的(您沒有指定它可以匹配任何其他次數而不只是一次),您是否有興趣保留它。所以括號是多餘的。

總而言之,這使得它m!^//#.*!。但是在正則表達式的末尾放一個未捕獲的.*(或任何帶有*量詞的東西)是毫無意義的,因爲它不會改變字符串是否匹配:*很樂意完全不匹配。

因此,你留下m!^//#!

至於從其他人解釋的文件中刪除行,請逐行讀取並打印所有要保留回另一個文件的行。如果你不這樣做,較大程序中,使用Perl的命令行開關可以輕鬆地做到這一點:

perl -ni.bak -e'print unless m!^//#!' somefile.txt 

這裏,-n開關使perl的把周圍的代碼的循環您提供將讀取所有的文件,你依次傳遞命令行。 -i開關(用於「in-place」)表示從腳本收集輸出並用它覆蓋每個文件的原始內容。 -i選項的.bak參數告訴perl將原始文件的備份保留在以原始文件名命名的文件中,並附加.bak。對於所有這些位,請參見perldoc perlrun

如果您想在較大的程序環境中執行此操作,安全地執行此操作最簡單的方法是打開文件兩次,一次用於讀取,另一次用IO::AtomicFile另一次寫入。IO :: AtomicFile將僅在成功關閉時才替換原始文件。

0

逐行讀取文件中的行,只寫那些行到一個新的文件,不要在正則表達式匹配刪除此行。 你不能只刪除一條線。

0

它從一開始就開始還是可以出現在任何地方?如果前者/舊/新是你想要的。如果是後者,我必須弄清楚。我懷疑可能會以某種方式使用退會。

0

我不認爲你的正則表達式是正確的。

首先,您需要以^開始,否則它將匹配該線路上任何位置的此模式。

其次,..應該是\/\/否則它會匹配任何兩個字符。

^\/\/#[^\n]*可能是你想要的。

然後做什麼EricSchaefer說,並逐行閱讀文件,只寫行不匹配。

-
BMB

0

嘗試以下操作:

perl -ne 'print unless m{^//#}' input.txt > output.txt 

如果您使用的是Windows,你需要雙引號來代替單引號。

你可以做同樣的使用grep

grep -v -e '^//#' input.txt > output.txt 
0

遍歷文件中的每一行,並跳過行,如果它的模式匹配:

 
my $fh = new FileHandle 'filename' 
    or die "Failed to open file - $!"; 

while (my $line = $fh->getline) { 
    next if $line =~ m{^//#}; 
    print $line; 
} 
close $fh; 

這將打印在所有行文件,但以'//#'開頭的行除外。

1

你真的不需要perl這個。

sed '/^\/\/#/d' inputfile > outputfile 

I < 3 sed。

2

過濾掉所有在一個文件中符合特定的正則表達式的所有行:

perl -n -i.orig -e 'print unless /^#/' file1 file2 file3 

-i開關後的「那些.orig」創建與給定的擴展名的文件的備份(那些.orig) 。如果你不需要備份,你可以跳過它(只需使用-i)。

-n開關導致perl執行文件中每行的指令(-e'...')。該行存儲在$ _中(這也是許多指令的默認參數,在這種情況下:print和regex匹配)。

最後,-e開關的參數表示「打印行,除非它在行的開頭匹配#字符。

PS。還有,其行爲類似於-n一個-p開關,除了線路總是打印(好用於搜索和替換)

2

正如其他人所指出的那樣,如果最終的目標是隻刪除開始//#線,出於性能考慮,您可能最好使用grepsed

grep -v '^\/\/#' filename.txt > filename.stripped.txt 

sed '/^\/\/#/d' filename.txt > filename.stripped.txt 

sed -i '/^\/\/#/d' filename.txt 

如果你喜歡就地編輯。

注意,在Perl您正則表達式將

m{^//#} 

它匹配兩個斜槓之後是#字符串的開始。

請注意,您可以使用匹配運算符m{pattern}而不是更熟悉的/pattern/來避免「反斜槓」。儘早訓練自己的語法,因爲這是避免過度轉義的簡單方法。您可以像m%^//#%m#^//\##一樣有效地編寫m{^//#},具體取決於您想要匹配的內容。力求清晰 - 正則表達式難以破譯,沒有可避免的反斜槓的多刺森林,導致可讀性下降。嚴重的是,m/^\/\/#/看起來像一條鱷魚,帶有切齒,填充物或阿爾卑斯山的小型ASCII畫。

腳本中可能出現的一個問題是,如果整個文件被篡改爲字符串,換行符和全部文件。爲了防範這種情況下,使用/ m(多)修改器的正則表達式:

m{^//#}m 

這使^匹配在換行符之後字符串的開始。你會認爲有一種方法可以去除或匹配匹配m{^//#.*$}的行,使用正則表達式修飾符/g/m/s,如果你已經將文件篡改爲字符串,但不想複製它(乞求首先爲什麼它被扯成一個字符串的問題。)它可能應該可能,但它遲到了,我沒有看到答案。然而,這樣做的一個「簡單」的方法是:

my $cooked = join qq{\n}, (grep { ! m{^//} } (split m{\n}, $raw)); 

即使創建一個副本而不是原始字符串$raw就地編輯。