1

假设我有一个关键字文件,其中包含“ab”、“ef”、“ab ef”(现实生活:另外一千个条目)。我想查看我的每个关键字出现的页面

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 ]

我知道如何非常低效地做到这一点。将文件slurp成一个长字符串。小写它。将其拆分为页面。对于每个页面,正则表达式每个(小写)关键字条目以查看它是否出现在页面上。如果是这样,则在包含我的关键字的哈希末尾添加页码作为键。不难,但令人痛苦地低效。我的算法看起来更像是一种 C 方法,而不是一种优雅的 perl 方法。

唉,我想不出更好的选择。我什至不能先将主文件读入散列,因为空格分隔的多词关键字的可能性会使这种不雅的错误逻辑。

也许 perl 是错误的工具?!

4

3 回答 3

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;
于 2013-10-01T22:57:20.447 回答
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
于 2013-10-01T22:37:22.337 回答
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
        ];

根据您的样本。

于 2013-10-01T22:29:07.830 回答