找到一个匹配并替换perl中的下一行

时间:2014-03-12 20:14:23

标签: perl

我正在处理perl脚本,需要一些帮助。要求是,我必须找到一个标签,一旦找到标签,我必须在标签后面的一行中替换该单词。例如,如果标签是ABC:

ABC:
string to be replaced 
some other lines
ABC: 
string to be replaced
some other lines
ABC: 
string to be replaced

我想写一个匹配标签的脚本(ABC),一旦找到标签,就在标签后面的下一行替换一个单词。

这是我的尝试:

open(my $fh, "<", "file1.txt") or die "cannot open file:$!";

while (my $line = <$fh>)) 
{
    next if ($line =~ /ABC/) {

    $line =~ s/original_string/replaced_string/;
  }
  else {
    $msg = "pattern not found \n ";
    print "$msg";
  }
}

这是正确的吗?任何帮助将不胜感激。

5 个答案:

答案 0 :(得分:1)

你在循环中这样做:

 next if ($line =~ /ABC/);

因此,您正在阅读该文件,如果该行中的任何位置包含ABC,则跳过该行。但是,对于每隔一行,您都要进行替换。最后,您正在更换所有其他行上的字符串并将其打印出来,而不是打印出您的标签。

这就是你所说的:

  • 我必须 阅读该文件,直到我 找到 标签
  • 找到标签后
  • 我必须 阅读下一行 替换标签后面一行中的单词。

所以:

  • 您希望逐行阅读文件。
    • 如果一行与标签匹配
      • 阅读下一行
      • 替换行上的文字
    • 打印出

遵循以下指示:

use strict;
use warnings;                   # Hope you're using strict and warnings
use autodie;                    # Program automatically dies on failed opens. No need to check
use feature qw(say);            # Allows you to use say instead of print


open my $fh, "<", "file1.txt";  # Removed parentheses. It's the latest style

while (my $line = <$fh>) {
    chomp $line;               # Always do a chomp after a read.
    if ( $line eq "ABC:" ) {   # Use 'eq' to ensure an exact match for your label
        say "$line";           # Print out the current line
        $line = <$fh>          # Read the next line
        $line =~ s/old/new/;   # Replace that word
   }
   say "$line";                # Print the line
}
close $fh;                     # Might as well do it right

请注意,当我使用say时,我不必将\n放在行尾。此外,通过阅读后我的chomp,我可以轻松匹配标签而无需担心最后的\n

这完全按照你说应该完成的方式完成,但有几个问题。首先,当我们$line = <$fh>时,我们无法保证我们真的在读一条线。如果文件在那里结束怎么办?

此外,在多个位置读取文件也是不好的做法。这使得维护程序变得更加困难。要解决此问题,我们将使用标志变量。这允许我们知道之前的行是否是标记:

use strict;
use warnings;                   # Hope you're using strict and warnings
use autodie;                    # Program automatically dies on failed opens. No need to check
use feature qw(say);            # Allows you to use say instead of print

open my $fh, "<", "file1.txt";  # Removed parentheses. It's the latest style

my $tag_found = 0;             # Flag isn't set
while (my $line = <$fh>) {
    chomp $line;               # Always do a chomp after a read.
    if ( $line eq "ABC:" ) {   # Use 'eq' to ensure an exact match for your label
        $tag_found = 1         # We found the tag!
    }
    if ( $tag_found ) {
        $line =~ s/old/new/;   # Replace that word
        $tag_found = 0;        # Reset our flag variable
    }
    say "$line";               # Print the line
}
close $fh;                     # Might as well do it right

当然,我更愿意消除神秘的价值。例如,标记应该是变量或常量。与您要搜索的字符串和要替换的字符串相同。

你提到这是一个单词,所以你的正则表达式替换应该是这样的:

$line =~ s/\b$old_word\b/$new_word/;

\b标记字边界。这样,如果你想用 dog 替换 cat 这个词,你就不会在一行上说:

The Jeopardy category is "Say what".

您不希望将category更改为dogegory

答案 1 :(得分:1)

以下单行将满足您的需求:

perl -pe '++$x and next if /ABC:/; $x-- and s/old/new/ if $x' inFile > outFile

代码设置一个标志,如果找到标签,则获取下一行。如果设置了标志,则取消设置并执行替换。

希望这有帮助!

答案 2 :(得分:0)

您的问题是读取文件不是那样的。你是逐行进行的,所以当你的正则表达式测试为true时,你想要改变的行还没有。您可以尝试添加一个布尔变量来检查最后一行是否为标签。

#!/usr/bin/perl;

use strict;
use warnings;

my $found;
my $replacement = "Hello";
while(my $line = <>){
    if($line =~ /ABC/){
        $found = 1;
        next;
    }
    if($found){
        $line =~ s/^.*?$/$replacement/;
        $found = 0;
        print $line, "\n";
    }
}

答案 3 :(得分:0)

或者你可以使用File :: Slurp并将整个文件读成一个字符串:

use File::Slurp;
$x = read_file( "file.txt" );
$x =~ s/^(ABC:\s*$ [\n\r]{1,2}^.*?)to\sbe/$1to was/mgx;         
print $x;

使用/ m使^和$匹配嵌入的开始/结束行
x是允许$后面的空格 - 可能有更好的方式
产率:

ABC:
string to was replaced 
some other lines
ABC: 
string to was replaced
some other lines
ABC: 
string to was replaced

答案 4 :(得分:0)

另外,依靠perl的in-place editing

use File::Slurp qw(read_file write_file);

use strict;
use warnings;

my $file = 'fakefile1.txt';

# Initialize Fake data
write_file($file, <DATA>);

# Enclosed is the actual code that you're looking for.
# Everything else is just for testing:
{
    local @ARGV = $file;
    local $^I = '.bac';

    while (<>) {
        print;
        if (/ABC/ && !eof) {
            $_ = <>;
            s/.*/replaced string/;
            print;
        }
    }

    unlink "$file$^I";
}

# Compare new file.
print read_file($file);

1;

__DATA__
ABC:
string to be replaced 
some other lines
ABC: 
string to be replaced
some other lines
ABC: 
string to be replaced
ABC: 

<强>输出

ABC:
replaced string
some other lines
ABC:
replaced string
some other lines
ABC:
replaced string
ABC: