2011-02-03 103 views
1

我想打開一個文件,搜索文件中的特定字符串以開始我的搜索,然後在文件中稍後對字符串執行替換。例如,我的文件看起來像:Perl編輯文件

測試舊

的Hello World

數據

Begin_search_here

新數據

舊數據

新數據

我要打開的文件,從「Begin_search_here」開始我的搜索,然後取代「舊」與「新」的一個實例。我的代碼如下所示,我正確地找到字符串,但出於某種原因,我沒有寫入正確的位置。

open(FILE, "+<$filename") || die "problem opening file"; 

my search = 0; 
while(my $line = <FILE>) 
{ 
if($line =~ m/Begin_search_here/) 
{ 
$search = 1; 
} 

if($search == 1 && $line =~m/Old/) 
{ 
$line = s/Old/New/; 
print FILE $line 
} 

close FILE; 

回答

4

這裏亞去:

local $^I = '.bak'; 
local @ARGV = ($filename); 
local $_; 
my $replaced = 0; 

while (<>) { 
    if (!$replaced && /Begin_search_here/ .. $replaced) { 
     $replaced = s/Old/New/; 
    } 
    print; 
} 

說明:

設置$^I變量能夠就地編輯,就像您已經運行帶有-i標誌的Perl。原始文件將被保存爲與原始文件相同的名稱,但擴展名爲「.bak」;如果您不想進行備份,請將".bak"替換爲""

@ARGV設置爲就地編輯的文件列表;這裏只是你在變量$filename中命名的單個文件。

$_已本地化,以防止在子程序中發生此代碼段時覆蓋此常用變量。

觸發器運算符..用於確定要執行替換的文件的哪部分。在第一次遇到匹配模式爲Begin_search_here的行時,它將爲假,然後將保持爲真,直到第一個發生替換的時間(記錄在變量$replaced中),當它關閉時。

1

您正在濫用隨機訪問文件模式。在更新$line並說print FILE $line時,文件句柄的「光標」已位於下一行行的開頭。所以原來的行不會改變,下一行會被覆蓋,而不是覆蓋原來的行。

就地編輯(見perlrun)看起來很適合這個問題。

否則,您需要詳細閱讀tell函數,在讀取一行之前保存文件位置,並在重寫該行之前將seek保存回該位置。哦,你寫的數據必須和你覆蓋的數據大小完全一致,否則你將完全取消你的文件 - 參見this question

0

This Works。如您所述,它只會替換一個事件。

#! /usr/bin/perl -pi.bak 

if (not $match_state) { 
    if (/Begin_search_here/) { 
    $match_state = "accepting"; 
    } 
} 
elsif ($match_state eq "accepting") { 
    if (s/Old/New/) { 
    $match_state = "done"; 
    } 
} 
1

你可能會通過打開輸入文件中讀取模式(open(my $fh, '<', $file) or die ...;),並寫入修改後的文本到一個臨時的輸出文件,然後複製臨時文件上層建築的輸入文件,當你要提供最好的服務完成了你的處理。

0

要非常小心地編輯文件。如果您要替換的數據長度不同,則會破壞文件。另外,如果你的程序在中間失敗,你最終會被銷燬的文件。

最好的選擇是讀每行,處理行,並將每行寫入一個新文件。這甚至可以讓你運行你的程序,檢查輸出,如果你有錯誤,修復它並重新運行程序。然後,一旦一切正常,請添加將新文件移動到舊名稱的步驟。

我一直在使用Perl自3.x版本,我想不出一次我修改文件到位

use strict; 
use warnings; 

open (INPUT, "$oldfile") or die qq(Can't open file "$oldFile" for reading); 
open (OUTPUT, "$oldfile.$$") or die qq(Can't open file "$oldfile.$$" for writing); 

my $startFlag = 0; 
while (my $line = <INPUT>) { 
    if ($line ~= /Begin_search_here/) { 
     $startFlag = 1; 
    } 
    if ($startFlag) { 
     $line =~ s/New/Old/; 
    } 
    print OUTPUT "$line"; 
} 

# 
# Only implement these two steps once you've tested your program 
# 
unlink $oldfile; 
rename $oldfile.$$", $oldfile; 
1

我已經做了一些類似這樣的編輯,我想出了一個通用的(但簡裝)策略:

use strict; 
use warnings; 

use English qw<$INPLACE_EDIT>; 
use Params::Util qw<_CODE>; 

local $INPLACE_EDIT = '.bak'; 
local @ARGV = '/path/to/file'; 

my @line_actions 
    = (qr/^Begin_search_here/ 
     , qr/^Old Data/ => sub { s/^Old/New/ } 
    ); 
my $match = shift @line_actions; 
while (<>) { 
    if ($match and /$match/) { 
     if (_CODE($line_actions[0])) { 
      shift(@line_actions)->($_); 
     } 
     $match = shift @line_actions; 
    } 
    print; 
}