【问题标题】:Find all the occurrence of string in a file and print its line number in Perl查找文件中所有出现的字符串并在 Perl 中打印其行号
【发布时间】:2012-12-06 10:42:28
【问题描述】:

我有一个包含 400000 行的大文件,每行包含许多由制表符分隔的关键字。

而且我还有一个文件,其中包含要匹配的关键字列表。说这个文件充当查找。

因此,对于查找表中的每个关键字,我需要在给定文件中搜索所有出现的关键字。并且应该打印出现的行号。

我试过了

#!usr/bin/perl
use strict;
use warnings;

my $linenum = 0;

print "Enter the file path of lookup table:";
my $filepath1 = <>;

print "Enter the file path that contains keywords :";
my $filepath2 = <>;

open( FILE1, "< $filepath1" );
open FILE2, "< $filepath2" ;

open OUT, ">", "SampleLineNum.txt";

while( $line = <FILE1> )
{
    while( <FILE2> ) 
    {
        $linenum = $., last if(/$line/);
    }
    print OUT "$linenum ";
}

close FILE1;

这给出了关键字的第一次出现。但我需要所有的出现,并且关键字应该完全匹配。

完全匹配面临的问题是,例如我有关键字“hello”和“hello world”

如果我需要匹配“hello”,它也会返回包含“hello world”的行号 我的脚本应该只匹配“hello”并给出它的行号。

【问题讨论】:

  • 简单:如果效率不是一个大问题,您可以将所有关键字从 file2 加载到一个数组中。然后遍历 file1 并在每一行搜索数组中的所有关键字。
  • 你能详细说明最后一段吗?是否要搜索“hello”以返回“hello world”?
  • @KarthikT 如果我搜索 hello world 它应该只匹配那个词而不是它的子字符串,比如 hello 。

标签: perl string-matching line-numbers


【解决方案1】:

这是一个匹配所有关键字每次出现的解决方案:

#!usr/bin/perl
use strict;
use warnings;

#Lexical variable for filehandle is preferred, and always error check opens.
open my $keywords,    '<', 'keywords.txt' or die "Can't open keywords: $!";
open my $search_file, '<', 'search.txt'   or die "Can't open search file: $!";

my $keyword_or = join '|', map {chomp;qr/\Q$_\E/} <$keywords>;
my $regex = qr|\b($keyword_or)\b|;

while (<$search_file>)
{
    while (/$regex/g)
    {
        print "$.: $1\n";
    }
}

keywords.txt:

hello
foo
bar

search.txt:

plonk
food is good
this line doesn't match anything
bar bar bar
hello world
lalalala
hello everyone

输出:

4: bar
4: bar
4: bar
5: hello
7: hello

说明:

这将创建一个匹配关键字文件中所有关键字的正则表达式。

&lt;$keywords&gt; - 在列表上下文中使用时,它返回文件所有行的列表。

map {chomp;qr/\Q$_\E/} - 这会从每一行中删除换行符并将\Q...\E quote-literal 正则表达式运算符应用于每一行(这样可以确保如果您有像“foo.bar”这样的关键字,它将把点视为文字字符,而不是正则表达式元字符)。

join '|', - 将结果列表连接成一个字符串,用竖线字符分隔。

my $regex = qr|\b($keyword_or)\b|; - 创建一个如下所示的正则表达式:

/\b(\Qhello\E|\Qfoo\E|\Qbar\E)\b/

此正则表达式将匹配您的任何关键字。 \b 是单词边界标记,确保只有整个单词匹配:food 不再匹配 foo。括号捕获与$1 匹配的特定关键字。这就是输出打印匹配关键字的方式。

我更新了解决方案以匹配给定行上的每个关键字,并且只匹配完整的单词。

【讨论】:

    【解决方案2】:

    这是更大的一部分吗?因为这是一个带有grep的单行代码

    grep -n hello filewithlotsalines.txt
    
    grep -n "hello world" filewithlotsalines.txt
    

    -n 获取 grep 以在匹配行之前首先显示行号。您可以通过man grep 获取更多选项。

    我在这里假设您使用的是 linux 或 *nix 系统。

    【讨论】:

    • 你能给我更多的解释吗?
    • @Sishanth 你可以用 grep 看一个例子
    • 这对于单个关键字来说很好,但 OP 希望匹配文件中的整个关键字列表。
    • @KarthikT,很公平。但是一旦你添加了一个循环和从文件中获取关键字的逻辑,grep 解决方案不会比 Perl 解决方案短。
    • @dan1111:错了。 grep -n -f keywords.txt filewithlotsalines.txt 从文件中获取关键字来搜索大文件。
    【解决方案3】:

    我对您的要求有不同的解释。似乎您可能想要维护一个行号列表,其中在“关键字”文件的行上可以找到查找表中的某些条目。这是一个示例查找表:

    hello world
    hello
    perl
    hash
    Test
    script
    

    还有一个制表符分隔的“关键字”文件,其中可以在一行中找到多个关键字:

    programming tests
    hello   everyone
    hello   hello world perl
    scripting   scalar
    test    perl    script
    hello world perl    script  hash
    

    鉴于以上情况,考虑以下解决方案:

    use strict;
    use warnings;
    
    my %lookupTable;
    
    print "Enter the file path of lookup table: \n";
    chomp( my $lookupTableFile = <> );
    
    print "Enter the file path that contains keywords: \n";
    chomp( my $keywordsFile = <> );
    
    open my $ltFH, '<', $lookupTableFile or die $!;
    
    while (<$ltFH>) {
        chomp;
        undef @{ $lookupTable{$_} };
    }
    
    close $ltFH;
    
    open my $kfFH, '<', $keywordsFile or die $!;
    
    while (<$kfFH>) {
        chomp;
        for my $keyword ( split /\t+/ ) {
            push @{ $lookupTable{$keyword} }, $. if defined $lookupTable{$keyword};
        }
    }
    
    close $kfFH;
    
    open my $slFH, '>', 'SampleLineNum.txt' or die $!;
    
    print $slFH "$_: @{ $lookupTable{$_} }\n"
      for sort { lc $a cmp lc $b } keys %lookupTable;
    
    close $slFH;
    
    print "Done!\n";
    

    输出到SampleLineNum.txt:

    hash: 6
    hello: 2 3
    hello world: 3 6
    perl: 3 5 6
    script: 5 6
    Test: 
    

    脚本使用数组散列 (HoA),其中键是查找表中的条目,关联的值是对行号列表的引用,其中该条目在“关键字”文件的行中找到.哈希 %lookupTable 使用对空列表的引用进行初始化。

    'keywords'文件的每一行在分隔选项卡上是split,如果在%lookupTable中定义了相应的条目,则行号是push在相应的列表中。完成后,%lookupTable 键被不区分大小写地排序并写出到SampleLineNum.txt,以及找到条目的相应行号列表(如果有)。

    没有对输入的文件名进行完整性检查,因此请考虑添加这些。

    希望这会有所帮助!

    【讨论】:

      【解决方案4】:

      要查找所有匹配项,您需要读入关键字,然后遍历关键字以查找每一行的匹配项。这是我修改后使用数组在行中查找关键字的内容。另外,我添加了一个计数器来计算行号,然后 如果有匹配项打印出行号。即使没有匹配项,您的代码也会为每一行打印一个项目。

      #!usr/bin/perl
      use strict;
      use warnings;
      
      my $linenum = 0;
      
      print "Enter the file path of lookup table:";
      my $filepath1 = <>;
      
      print "Enter the file path that contains keywords :";
      my $filepath2 = <>;
      
      open( FILE1, "< $filepath1" );
      open FILE2, "< $filepath2" ;
      
      # Read in all of the keywords
      my @keywords = <FILE2>; 
      
      # Close the file2
      close(FILE2);
      
      # Remove the line returns from the keywords
      chomp @keywords;
      
      # Sort and reverse the items to compare the maximum length items
      # first (hello there before hello)
      @keywords = reverse sort @keywords;
      
      foreach my $k ( @keywords)
      {
        print "$k\n";
      }
      open OUT, ">", "SampleLineNum.txt";
      my $line;
      # Counter for the lines in the file
      my $count = 0;
      while( $line = <FILE1> )
      {
          # Increment the counter for the number of lines
          $count++;
          # loop through the keywords to find matches
          foreach my $k ( @keywords ) 
          {
              # If there is a match, print out the line number 
              # and use last to exit the loop and go to the 
              # next line
              if ( $line =~ m/$k/ ) 
              {
                  print "$count\n";
                  last;
              }
          }
      }
      
      close FILE1;
      

      【讨论】:

        【解决方案5】:

        我认为有一些类似的问题。您可以查看:

        File::Grep 模块很有趣。

        【讨论】:

          【解决方案6】:

          因为其他人已经给出了一些 perl 解决方案,我建议你可能在这里使用 awk。

          > cat temp
          abc
          bac
          xyz
          
          > cat temp2
          abc     jbfwerf kfnm
          jfjkwebfkjwe    bac     xyz
          ndwjkfn abc kenmfkwe    bac     xyz
          

          > awk 'FNR==NR{a[$1];next}{for(i=1;i<=NF;i++)if($i in a)print $i,FNR}' temp temp2
          abc 1
          bac 2
          xyz 2
          abc 3
          bac 3
          xyz 3
          >
          

          【讨论】:

            猜你喜欢
            • 2013-01-23
            • 2015-08-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多