2013-10-01 25 views
1

比方說,我有一個關鍵字文件,其中包含「ab」,「ef」,「ab ef」(真實生活:另外一千個條目)。我想查什麼網頁我的每一個關鍵詞都出現一個真正的索引--- perl中的多個匹配

page 0 
ab 
gg 
^L 
page 1 
ab ef 
^L 
page 2 
Ab 
ef 
[another thousand pages, 2 million words, each ^L separated] 

我想回去

ab => [ 0,1,2 ] 
ef => [ 1,2 ] 
ab ef => [ 1 ] 

我知道如何做到這一點的效率非常低。將文件翻成長字符串。小寫它。將其分成頁面。對於每個頁面,將每個(小寫)關鍵字條目正則表達以查看它是否出現在頁面上。如果是,則在包含我的關鍵字作爲關鍵字的散列末尾添加頁碼。不難,但痛苦無效。我的算法看起來更像是一種C方法,而不是一種優雅的Perl方法。

唉,我想不出一個更好的選擇。我甚至無法首先將主文件讀入散列,因爲空格分隔的多詞關鍵字可能會導致這種不合邏輯的錯誤。

也許perl是這個錯誤的工具?!

回答

1

這非常適合Perl;反過來看看它。使用每個關鍵字,將當前頁面添加到該單詞的頁面列表中。創建單詞的頁面列表,如果它是我們見過的第一個。

use strict; 
use warnings; 

use Data::Dumper; 


my %keywords =(); 
my $page = 0; 

while (<>) 
{ 
    chomp; # remove newline 

    if (/^page \d+$/) # skip "page 0", etc. 
    { 
     next; 
    } 
    elsif (/^\l$/)  # ctrl-L: new page 
    { 
     ++$page; 
    } 
    else 
    { 
     my $word = lc($_); 

     addWord($word); 

     if ($word =~ /\s/) 
     { 
      my @parts = split(/\s+/, $word); 

      for my $part (@parts) 
      { 
       addWord($part); 
      } 
     } 
    } 
} 

print Dumper(%keywords); 


sub addWord 
{ 
    my ($word) = @_; 

    # haven't seen this word? start an empty page list 
    if (! defined($keywords{$word})) 
    { 
     $keywords{$word} = []; 
    } 

    # add current page to the word's list 
    push @{ $keywords{$word} }, $page; 
} 

打印:

$VAR1 = 'ef'; 
$VAR2 = [ 
      1, 
      2 
     ]; 
$VAR3 = 'gg'; 
$VAR4 = [ 
      0 
     ]; 
$VAR5 = 'ab'; 
$VAR6 = [ 
      0, 
      1, 
      2 
     ]; 
$VAR7 = 'ab ef'; 
$VAR8 = [ 
      1 
     ]; 

根據您的樣品。

1

完美的使用perl。

輸出以下:

ab => [ 0,1,2 ] 
ab ef => [ 1 ] 
ef => [ 1,2 ] 
gg => [ 0 ] 

的代碼:

#!/usr/bin/env perl 

use warnings; 
use strict; 

main(); 
1; 

sub main { 
    my $data = {}; 
    my $page = 0; 
    while (<DATA>) { 
     chomp; 
     next if /\A\^L/; 
     if (/\Apage (\d+)/) { 
      $page = $1; 
     } else { 
      my $line = lc($_); 
      $data->{$line}->{$page}++; 
      for (split /\s/, $line) { 
       $data->{$_}->{$page}++; 
      } 
     } 
    } 

    for my $keyword (sort keys %$data) { 
     my @pages = sort {$a <=> $b} keys %{$data->{$keyword}}; 
     print $keyword . ' => [ ' . join(',',@pages) . ' ]' . "\n"; 
    } 
} 

__DATA__ 
page 0 
ab 
gg 
^L 
page 1 
ab ef 
^L 
page 2 
Ab 
ef 
4

其他的答案採用不必要的複雜裝置。這個問題的關鍵是理解我們可以將結束$/的行設置爲我們喜歡的任何行。 ^L代表形式飼料\f

use strict; use warnings; 
use List::MoreUtils qw/uniq/; 

my %keywords; 
local $/ = "\f"; # set the input record separator (aka. line end) to form feed 

while (<>) { 
    chomp; # remove the \f 
    my ($page, $body) = split /\n/, $_, 2; # seperate the page header 
    my $page_no = ($page =~ /([0-9]+)/) ? $1 # extract the page number 
       : die "Header did not contain a number:\n$page\n"; 

    # split body into lines, and lines into words. Keep only unique KWs: 
    my @words = uniq map { $_, split } split /\n/, lc $body; 
    # Map KWs to an array of page №s. 
    push @{ $keywords{$_} }, $page_no for @words; 
} 

# Output the data: 
use Data::Dumper; 
print Dumper \%keywords; 
+0

你需要在uniq之前做lc(理想情況下只在身體上) – ysth

+0

我不認爲它解決了這個問題。它將文件轉換爲其組成單詞,併爲每個單詞分配一個頁碼。 –

+0

我不認爲它解決了這個問題。它將文件轉換爲其組成單詞,併爲每個單詞分配一個頁碼。它不是從包含可包含空格的短語的單詞列表開始的。我的\\page = split(/ f /,slurp($ filename));我不會更快做 my \ @page = split我的$ pageno(0 .. $#頁面){ my \ @wordsonpage = split/[\ n \ s] /,$ page [$ pageno]; ; push @ {words {$ _}},$ pageno for \ @wordsonpage } #現在我們有一個帶頁碼的單詞散列 –

相關問題