【问题标题】:Amend perl script so that words are matched on a word for word basis修改 perl 脚本,以便单词逐字匹配
【发布时间】:2016-09-26 04:21:18
【问题描述】:

我一直在使用这个 perl 脚本(感谢 Jeff Schaller)来匹配两个单独 csv 文件的标题字段中的 3 个或更多单词。 这里的原始问题:

https://unix.stackexchange.com/questions/283942/matching-3-or-more-words-from-fields-in-separate-csv-files?noredirect=1#comment494461_283942

我还根据 meuh 的建议添加了一些异常功能:

#!/bin/perl

my @csv2 = ();
open CSV2, "<csv2" or die;
@csv2=<CSV2>;
close CSV2;

my %csv2hash = ();
for (@csv2) {
  chomp;
  my ($title) = $_ =~ /^.+?,\s*([^,]+?),/; #/ match the title 
  $csv2hash{$_} = $title;
}

open CSV1, "<csv1" or die;
while (<CSV1>) {
  chomp;
  my ($title) = $_ =~ /^.+?,\s*([^,]+?),/; #/ match the title 
  my @titlewords = split /\s+/, $title;    #/ get words

  my @new;                          #add exception words which shouldn't be matched
  foreach my $t (@titlewords){
  push(@new, $t) if $t !~ /^(and|if|where)$/i;
  }
  @titlewords = @new;
  my $desired = 3;
  my $matched = 0;
  foreach my $csv2 (keys %csv2hash) {
    my $count = 0;
    my $value = $csv2hash{$csv2};
    foreach my $word (@titlewords) {
      ++$count if $value =~ /\b$word\b/i;
      last if $count >= $desired;
    }
    if ($count >= $desired) {
      print "$csv2\n";
      ++$matched;
    }
  }
  print "$_\n" if $matched;
}
close CSV1;

在我的测试过程中,我发现我想要调整的一个问题是,如果 csv2 包含单个常用词,例如 the,如果在 csv1 中重复三次或更多次,则会找到三个正匹配.澄清一下:

如果 csv1 包含:

1216454,the important people feel the same way as the others, 15445454, 45445645

^ 即上面的行中有三个the 不满足

如果 csv2 包含:

14564564,the tallest man on earth,546456,47878787

^ 即这一行中有一个the 的实例

然后我希望只有一个单词被归类为匹配,并且没有输出(基于我想要的匹配单词数量 - 3),因为其中一个文件中只有一个匹配单词的实例。

但是如果:

csv1 包含:

1216454,the important people feel the same way as the others,15445454, 45445645

和 csv2 包含:

15456456,the only way the man can sing the blues,444545,454545

然后,由于每个单词中有三个匹配的单词(即每个标题中单词 the 的 3 个实例,那么我希望根据我想要的匹配单词数量为 3 或更多,从而生成输出:

1216454,the important people feel the same way as the others,15445454, 45445645
15456456,the only way the man can sing the blues,444545,454545

我想修改脚本,以便如果 csv 中有一个单词的一个实例,而另一个 csv 中有同一个单词的多个实例,那么它被归类为只有一个匹配项。但是,如果在两个文件中都有单词 the 的 3 个实例,那么它仍应归类为三个匹配项。基本上我希望匹配是逐字逐句的。 除了这个之外,关于剧本的一切都是完美的,所以我宁愿不完全回到绘图板上,因为我对除此之外的一切都很满意。 我希望我已经解释过了,如果有人需要任何澄清,请告诉我。

【问题讨论】:

  • edit您的问题,并给我们一个您需要解析的输入和您希望看到的输出的示例。指向您之前的问题的链接也将有所帮助。我认为您想要的是仅计算唯一匹配项,而忽略相同单词的重复。对吗?

标签: text-processing perl


【解决方案1】:

如果您只想计算唯一匹配,您可以使用哈希而不是列表来收集来自 csv1 的单词,就像您为 csv2 所做的那样,然后还分别计算每个单词的出现次数:

#!/usr/bin/env perl

my @csv2 = ();
open CSV2, "<csv2" or die;
@csv2=<CSV2>;
close CSV2;

my %csv2hash = ();
for (@csv2) {
  chomp;
  my ($title) = $_ =~ /^.+?,\s*([^,]+?),/; #/ match the title 
  $csv2hash{$_} = $title;
}

open CSV1, "<csv1" or die;
while (<CSV1>) {
  chomp;
  my ($title) = $_ =~ /^.+?,\s*([^,]+?),/; #/ match the title 
    my %words;
    $words{$_}++ for split /\s+/, $title;    #/ get words
    ## Collect unique words
    my @titlewords = keys(%words);
  my @new;                          #add exception words which shouldn't be matched
  foreach my $t (@titlewords){
        push(@new, $t) if $t !~ /^(and|if|where)$/i;
  }
  @titlewords = @new;
  my $desired = 3;
  my $matched = 0;
  foreach my $csv2 (keys %csv2hash) {
    my $count = 0;
    my $value = $csv2hash{$csv2};
    foreach my $word (@titlewords) {
            my @matches   = ( $value=~/\b$word\b/ig );
            my $numIncsv2 = scalar(@matches);
            @matches      = ( $title=~/\b$word\b/ig );
            my $numIncsv1 = scalar(@matches);
            ++$count if $value =~ /\b$word\b/i;
            if ($count >= $desired || ($numIncsv1 >= $desired && $numIncsv2 >= $desired)) {
                $count = $desired+1;
                last;
            }
    }
    if ($count >= $desired) {
      print "$csv2\n";
      ++$matched;
    }
  }
  print "$_\n" if $matched;
}
close CSV1;

【讨论】:

  • 嗨,terdon,这更接近我想要实现的目标,但是请查看我对我的问题的编辑以及示例输出。目前,您的修改将只匹配给定单词的 1 个实例然后继续,但如果两个文件都包含同一个单词的多个实例,我希望它们算作匹配。
  • @nmh 好的,请参阅更新的脚本。下一次,请确保包含足够的示例以供我们理解。向我们展示重现所有可能情况的样本数据,并解释您需要从中获得什么输出。此外,这种复杂的问题对网站来说确实不好。如果您的问题的答案是完整的脚本,那么您需要将问题分成更小的块,以便您自己编写脚本。
  • 好的,明白了。我最初的问题确实从我尝试使用 grep 开始,但后来演变成一个我知之甚少的 perl 脚本!感谢您的帮助,我会在以后尝试拆分我的查询。
  • 嗨,terdon,我一直在经常阅读这个脚本,我不知道是不是只有我一个人,但我很难阅读 perl,即使我把它分解了。我想知道您是否可以在答案中添加一些额外的 # cmets,以便我了解它是如何工作的?
猜你喜欢
  • 2016-02-05
  • 1970-01-01
  • 2019-04-15
  • 2019-05-11
  • 2019-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多