使用 Perl 打印多行
Using Perl to print multiple lines
此代码从我拥有的文本文件中获取关键字 'fun',然后打印关键字前后的 20 个字符。但是,我还希望它打印前两行和后两行,但我不确定该怎么做。我不确定用这个更改代码或一次读取整个文件是否更容易。
{my $inputfile = "file";
$searchword = 'fun';
open (INPUT, '<', $inputfile) or die "fatal error reading the file \n";
while ($line1=<INPUT>)
{
#read in a line of the file
if ($line1 =~m/$searchword/i)
{print "searchword found\n";
$keepline = $line1;
$goodline =1;
$keepline =~/(.{1,20})(fun)(.{1,20})/gi;
if ($goodline==1)
{&write_excel};
$goodline =0;
}
你的代码似乎是
- 在'pledge'的每一边取20个字符,而不是
$searchword
;
- 开头有一个不匹配的'{';
- 不打印除我们无法检查的 &write_excel 的任何文件内容;和
- 有逻辑问题,如果找到
$searchword
,$goodline
无条件设置为'1',然后测试是否为'1',最后重置为'0'
撇开这个不谈,关于是否读入整个文件的问题取决于你的情况——你要搜索的文件有多大,你的机器有足够的内存;机器是共享资源等等。我假设你可以阅读整个文件,因为这是我经验中更常见的立场(那些不同意的人请记住(a)我承认它有争议;(b)它非常依赖于只有 OP 知道的情况)
鉴于此,有多种方法可以读取整个文件,但 consensus seems to be 与模块 File::Slurp
一起使用。给定这些参数,答案如下所示;
#!/usr/bin/env perl
use v5.12;
use File::Slurp;
my $searchword = 'fun';
my $inputfile = "file.txt";
my $contents = read_file($inputfile);
my $line = '\N*\n';
if ( $contents =~ /(
$line?
$line?
\N* $searchword \N* \n?
$line?
$line?
)/x) {
say "Found:\n" . ;
}
else {
say "Not found."
}
如果文件不存在(或其他问题),File::Slurp
会打印一条合理的错误消息,因此我省略了典型的 or die...
。每当使用正则表达式时——尤其是当你试图匹配多行内容时,使用 "extended mode"(通过在最后一个“/”之后放置一个 'x')来允许 insignificant 正则表达式中的空格。这样可以使布局更清晰。
为了更加清晰,我还分离了一行的定义,它由 0 个、1 个或多个非换行符组成,\N*
,后跟一个新行,\n
。但是,如果您的目标位于第一行、第二行、倒数第二行或最后一行,我认为您仍然需要该信息,因此可以选择匹配所请求的前后两行。 $line?
请注意,正则表达式是迂腐的,不可避免地 'fine details' 会影响成功匹配与不需要的匹配的定义 - 即。不要指望这 完全 在所有情况下都能满足您的要求。预计您将不得不进行一些试验和调整。
我不确定我是否理解您的代码块("pledge" 有什么用途?&write_excel
是什么?),但我可以自己回答您的问题。
首先,这个grep命令可以接受吗?它更快更干净:
grep -i -C2 --color "fun" "file"
-C NUM
标志告诉 grep
为每个模式匹配提供 NUM 行上下文。显然,--color
是可选的,但它可以帮助您在很长的行中找到匹配项。
否则,这里有一点 perl:
#!/usr/bin/perl
my $searchword = "fun";
my $inputfile = "file";
my $blue = "\e[1;34m"; # change output color to blue
my $green = "\e[1;32m"; # change output color to green
my $nocolor = "\e[0;0m"; # reset output to no color
my $prev1 = my $prev2 = my $result = "";
open (INPUT, '<', $inputfile) or die "fatal error reading the file \n";
while(<INPUT>) {
if (/$searchword/i) {
$result .= $prev2 . $prev1 . $_; # pick up last two lines
$prev2 = $prev1 = ""; # prevent reusing last two lines
for (1..2) { # for two more non-matching lines
while (<INPUT>) { # parse them to ensure they don't match
$result .= $_; # pick up this line
last unless /$searchword/i; # reset counting if it matched
}
}
} else {
$prev2 = $prev1; # save last line as $prev2
$prev1 = $_; # save current line as $prev1
}
}
close $inputfile;
exit 1 unless $result; # return with failure if without matches
$result =~ # add colors (okay to remove this line)
s/([^\e]{0,20})($searchword)([^\e]{0,20})/$blue$green$blue$nocolor/g;
print "$result"; # print the result
print "\n" unless $result =~ /\n\Z/m; # add newline if there wasn't already one
错误:这假设前后两行实际上是 20+ 个字符。如果您需要修复此问题,请将其放入 else
节。
此代码从我拥有的文本文件中获取关键字 'fun',然后打印关键字前后的 20 个字符。但是,我还希望它打印前两行和后两行,但我不确定该怎么做。我不确定用这个更改代码或一次读取整个文件是否更容易。
{my $inputfile = "file";
$searchword = 'fun';
open (INPUT, '<', $inputfile) or die "fatal error reading the file \n";
while ($line1=<INPUT>)
{
#read in a line of the file
if ($line1 =~m/$searchword/i)
{print "searchword found\n";
$keepline = $line1;
$goodline =1;
$keepline =~/(.{1,20})(fun)(.{1,20})/gi;
if ($goodline==1)
{&write_excel};
$goodline =0;
}
你的代码似乎是
- 在'pledge'的每一边取20个字符,而不是
$searchword
; - 开头有一个不匹配的'{';
- 不打印除我们无法检查的 &write_excel 的任何文件内容;和
- 有逻辑问题,如果找到
$searchword
,$goodline
无条件设置为'1',然后测试是否为'1',最后重置为'0'
撇开这个不谈,关于是否读入整个文件的问题取决于你的情况——你要搜索的文件有多大,你的机器有足够的内存;机器是共享资源等等。我假设你可以阅读整个文件,因为这是我经验中更常见的立场(那些不同意的人请记住(a)我承认它有争议;(b)它非常依赖于只有 OP 知道的情况)
鉴于此,有多种方法可以读取整个文件,但 consensus seems to be 与模块 File::Slurp
一起使用。给定这些参数,答案如下所示;
#!/usr/bin/env perl
use v5.12;
use File::Slurp;
my $searchword = 'fun';
my $inputfile = "file.txt";
my $contents = read_file($inputfile);
my $line = '\N*\n';
if ( $contents =~ /(
$line?
$line?
\N* $searchword \N* \n?
$line?
$line?
)/x) {
say "Found:\n" . ;
}
else {
say "Not found."
}
如果文件不存在(或其他问题),File::Slurp
会打印一条合理的错误消息,因此我省略了典型的 or die...
。每当使用正则表达式时——尤其是当你试图匹配多行内容时,使用 "extended mode"(通过在最后一个“/”之后放置一个 'x')来允许 insignificant 正则表达式中的空格。这样可以使布局更清晰。
为了更加清晰,我还分离了一行的定义,它由 0 个、1 个或多个非换行符组成,\N*
,后跟一个新行,\n
。但是,如果您的目标位于第一行、第二行、倒数第二行或最后一行,我认为您仍然需要该信息,因此可以选择匹配所请求的前后两行。 $line?
请注意,正则表达式是迂腐的,不可避免地 'fine details' 会影响成功匹配与不需要的匹配的定义 - 即。不要指望这 完全 在所有情况下都能满足您的要求。预计您将不得不进行一些试验和调整。
我不确定我是否理解您的代码块("pledge" 有什么用途?&write_excel
是什么?),但我可以自己回答您的问题。
首先,这个grep命令可以接受吗?它更快更干净:
grep -i -C2 --color "fun" "file"
-C NUM
标志告诉 grep
为每个模式匹配提供 NUM 行上下文。显然,--color
是可选的,但它可以帮助您在很长的行中找到匹配项。
否则,这里有一点 perl:
#!/usr/bin/perl
my $searchword = "fun";
my $inputfile = "file";
my $blue = "\e[1;34m"; # change output color to blue
my $green = "\e[1;32m"; # change output color to green
my $nocolor = "\e[0;0m"; # reset output to no color
my $prev1 = my $prev2 = my $result = "";
open (INPUT, '<', $inputfile) or die "fatal error reading the file \n";
while(<INPUT>) {
if (/$searchword/i) {
$result .= $prev2 . $prev1 . $_; # pick up last two lines
$prev2 = $prev1 = ""; # prevent reusing last two lines
for (1..2) { # for two more non-matching lines
while (<INPUT>) { # parse them to ensure they don't match
$result .= $_; # pick up this line
last unless /$searchword/i; # reset counting if it matched
}
}
} else {
$prev2 = $prev1; # save last line as $prev2
$prev1 = $_; # save current line as $prev1
}
}
close $inputfile;
exit 1 unless $result; # return with failure if without matches
$result =~ # add colors (okay to remove this line)
s/([^\e]{0,20})($searchword)([^\e]{0,20})/$blue$green$blue$nocolor/g;
print "$result"; # print the result
print "\n" unless $result =~ /\n\Z/m; # add newline if there wasn't already one
错误:这假设前后两行实际上是 20+ 个字符。如果您需要修复此问题,请将其放入 else
节。