網頁

2022年5月30日 星期一

正規式的應用:刪除換行符

假日接到以前同事的來電,要我幫忙處理一個檔案問題,檔案內容如下(共1百78萬多行):
要將單數行的資料移到偶數行尾端,變成下面形式:

如果用程式來跑就是將41行開始到檔案最後的單數行換行符號(\n)拿掉,偶數行自然就會接在單數行後面,然後將結果輸出到新檔案。
Perl程式如下:
#!/usr/bin/perl
$file_name = shift @ARGV;
print "Input file:$file_name\n";
open(DATA, '<', $file_name) or die $!;
$new_file = "new_".$file_name;
open(OUT, ">$new_file");
$line_count = 1;
while(<DATA>) {
  if ($line_count <= 40) {
    print OUT $_
  }
  if ($line_count > 40) {
    if (($line_count % 2 != 0) && (! /^END/)) {
      #chomp; #也可以這樣寫
      s/\n//g;
      print OUT $_;
    }
    else {
      print OUT $_;
    }
  }
  $line_count += 1;
}
print "Output file:$new_file\n";
 

程式執行大約花費2.23s

這程式有個小缺點就是如果要刪除換行的字串不是從40行開始就要修改一次程式,所以我想用正規式來解決這問題。
首先要定義這個字串,下面這個字串:
01XXXXX1XXXX XX XXXX XXXXXXXX X XXXXXXX X XXXXXXX X 10X1X1X XXXXX XX X
和這字串:
XXX X XX XX; /* 1 */
的差異是要移除換行符號的字串開頭都是0或1然後後面接著X或H或L組成的12個字元的樣式,後面不管什麼一直到結尾,因此我定義為:
's/(^[01XHL]{12}\s.*)\n/$1/g'

 
在Perl搜尋替換的語法是:s/搜尋樣式/替換字/g
搜尋樣式^是開頭,[ ]括號括起來的是0或1或X或H或L,{ }裡面數字表示出現12次,然後\s表示空白,. 是不管什麼字元,*是匹配0次或多次的任何字符,
( )小括號括起的是放進分組,接著是\n換行。
替換字$1表示用第一個分組替換。


因此可以寫成:(原始檔用.bak備份)
perl -p -i.bak -w -e 's/(^[01XHL]{12}\s.*)\n/$1/g' file_name
執行結果花費3.3s

接著用 diff 指令檢查2個結果是否相同 :
沒有輸出就是表示2個檔案內容是相同的。

雖然用正規式處理會稍微多花費一些時間,但是會很靈活,對於處理這類文字樣式的檔案是非常好的利器,你該不會想按90萬次 Del 鍵吧。