比较2个哈希中的2个已处理键

时间:2012-02-23 01:18:52

标签: string perl hash

我想在文件中读取一些符号,如“!”和“^”并希望在将它们与另一行的其他字符串进行比较之前删除它们。如果删除符号后两个字符串相同,我想将它们存储在另一个名为“common”的哈希中。 例如... FILEA:

hello!world
help?!3233
oh no^!!
yes!

FILEB:

hello
help?
oh no
yes

在这种情况下,FileA和FileB应该相同,因为我将字符比较到“!”的位置或“^”出现。 我使用以下代码读取文件:

open FILEA, "< script/".$fileA or die;
my %read_file;
while (my $line=<FILEA>) {
   (my $word1,my $word2) = split /\n/, $line;
   $word1 =~ s/(!.+)|(!.*)|(\^.+)|(\^.*)//;#to remove ! and ^
   $read_file{$word1} = $word1;
}
close(FILEA);

我打印出哈希中的键,它显示正确的结果(即它将FileA转换为“你好,帮助?,哦不,是)。但是,当我使用以下方法对FileA和FileB进行比较时代码,它总是失败。

while(($key,$value)=each(%config))
{
    $num=keys(%base_config);
    $num--;#to get the correct index
    while($num>=0)
    {
        $common{$value}=$value if exists $read_file{$key};#stored the correct matches in %common
        $num--;
    }
}

我尝试测试我的替换并使用以下示例比较两个字符串,并且它有效。我不知道为什么它不能从字符串中读取字符串中的字符串。

use strict;
use warnings;

my $str="hello^vsd";
my $test="hello";
$str =~ s/(!.+)|(!.*)|(\^.+)|(\^.*)//;
my %hash=();
$hash{$str}=();
foreach my $key(keys %hash)
{
    print "$key\n";
}
print "yay\n" if exists $hash{$test};
print "boo\n" unless exists $hash{$test};

两个文件可以具有不同数量的文本行,并且搜索时文本行不需要具有相同的顺序。即。 “哦不”可以在“你好”之前来到。

4 个答案:

答案 0 :(得分:0)

你可以使用正则表达式字符类s / [?^] // g来删除^和?,注意^需要是组中的最后一个,或者你需要转义它。 (如果你以后添加其他字符,可能会更安全地逃避它,所以它们不会被否定)。

我处理所有文件,使用哈希来计算单词存在的文件。

为了比较差异,我使用2 **(文件数量),因此我得到值2 ** 0 = 1,2 ** 1 = 2,2 ** 2 = 4,依此类推。我用来显示字符串属于哪个文件。如果它们总共存在,则它们将等于总文件数,因此在这种情况下为2 - 3(2 + 1)表示它们在两个文件中,1表示仅FileA,2表示FileB。你可以通过按位(&amp;)来检查这个。

编辑:添加了测试条件

<!-- language: perl -->

my @files = qw(FileA.txt FileB.txt);
my %words;
foreach my $i (0 .. $#files) {
    my $file = $files[$i];
    open(FILE,$file) or die "Error: missing file $file\n$!\n";
    while (<FILE>) {
        chomp;
        next if /^$/;
        my ($word) = split /[!\^]/;
        $word =~ s/[?\^]//g; # removes ^ and ?
        $words{$word} += 2**$i;
    }
    close(FILE);
}

my %common;
foreach my $key (sort keys %words) {
    my @found;
    foreach my $i (0 .. $#files) {
        if ( $words{$key} & 2**$i ) { push @found, $files[$i] }
    }
    if ( $words{$key} & 2**$#files ) { $common{$key}++ }
    printf "%10s %d: @found\n",$key,$words{$key};
}

my @tests = qw(hello^vsd chuck help? test marymary^);
print "\nTesting Words: @tests\n";
foreach (@tests) {
    my ($word) = split /[!\^]/;
    $word =~ s/[?\^]//g; # removes ^ and ?
    if ( exists $common{ $word } ) {
        print "Found: $word\n";
    }
    else {
        print "Cannot find: $word\n";
    }
}

输出:

    bahbah 2: FileB.txt
   chucker 1: FileA.txt
     hello 3: FileA.txt FileB.txt
      help 3: FileA.txt FileB.txt
  marymary 2: FileB.txt
     oh no 3: FileA.txt FileB.txt
      test 1: FileA.txt
       yes 3: FileA.txt FileB.txt

Testing Words: hello^vsd chuck help? test marymary^
Found: hello
Cannot find: chuck
Found: help
Cannot find: test
Found: marymary

答案 1 :(得分:0)

这是另一种同时读取两个文件的解决方案(假设两个文件的行数相同):

use strict;
use warnings;

our $INVALID = '!\^'; #regexp character class, must escape

my $fileA = "file1.txt";
my $fileB = "file2.txt";

sub readl
{
  my $fh = shift;
  my $ln = "";

  if ($fh and $ln = <$fh>)
  {
    chomp $ln;
    $ln =~ s/[$INVALID]+.*//g;
  }

  return $ln;
}

my ($fhA, $fhB);
my ($wdA, $wdB);
my %common = ();

open $fhA, $fileA or die "$!\n";
open $fhB, $fileB or die "$!\n";

while ($wdA = readl($fhA) and $wdB = readl($fhB))
{
  $common{$wdA} = undef if $wdA eq $wdB;
}

print "$_\n" foreach keys %common;

<强>输出

andrew@gidget:comparefiles$ cat file1.txt 
hello!world
help?!3233
oh no^!!
yes!

andrew@gidget:comparefiles$ cat file2.txt 
hello
help?
oh no
yes

andrew@gidget:comparefiles$ perl comparefiles.pl 
yes
oh no
hello
help?

答案 2 :(得分:0)

首先将可重用的段打包成子例程:

sub read_file {
    open my $fh, "<", $_[0] or die "read_file($_[0]) error: $!";
      # lexical handles auto-close when they fall out of scope
      # and detailed error messages are good
    my %file;
    while (my $line = <$fh>) {
        chomp $line;          # remove newline
        $line =~ s{[!^].*}{}; # remove everything starting from ! or ^
        $file{$line}++;
    }
    \%file
}

read_file获取输入文件名,并在任何!^字符之前返回线段的哈希值。每个线段都是一个键,值是它出现的次数。

使用它,下一步是找出文件之间匹配的行:

my ($fileA, $fileB) = map {read_file $_} your_file_names_here();

my %common;
$$fileA{$_} and $common{$_}++ for keys %$fileB;

print "common: $_\n" for keys %common;

将打印:

common: yes
common: oh no
common: hello
common: help?

如果您想测试它,可以按如下方式定义your_file_names_here

sub your_file_names_here {\(<<'/A', <<'/B')}
hello!world
help?!3233
oh no^!!
yes!
/A
hello
help?
oh no
yes
/B

答案 3 :(得分:0)

首先,我们必须规范您的输入。下面的代码为每个路径创建一个哈希。对于给定文件中的每一行,请删除以第一个!^字符开头的所有内容并记录其存在。

sub read_inputs {
  my @result;

  foreach my $path (@_) {
    my $data = {};

    open my $fh, "<", $path or die "$0: open $path: $!";
    while (<$fh>) {
      chomp;
      s/[!^].*//;  # don't put the caret first without escaping!
      ++$data->{$_};
    }

    push @result, $data;
  }

  wantarray ? @result : \@result;
}
Computing the intersection of two arraysData Manipulation部分涵盖了

Perl FAQ list。根据您的情况调整技术,我们想知道所有输入共有的行。

sub common {
  my %matches;
  for (@_) {
    ++$matches{$_} for keys %$_;
  }

  my @result = grep $matches{$_} == @_, keys %matches;
  wantarray ? @result : \@result;
}

将其与

结合在一起
my @input = read_inputs "FileA", "FileB";
my @common = common @input;
print "$_\n" for sort @common;

给出

的输出
hello
help?
oh no
yes