【问题标题】:How to sort rows in a text file in Perl?如何在 Perl 中对文本文件中的行进行排序?
【发布时间】:2011-04-04 23:18:22
【问题描述】:

我有几个看起来像这样的文本文件(A.txtB.txt)(每个可能有 ~10000 行)

processa,id1=123,id2=5321
processa,id1=432,id2=3721
processa,id1=3,id2=521
processb,id1=9822,id2=521
processa,id1=213,id2=1
processc,id1=822,id2=521

我需要检查文件A.txt 中的每一行是否也存在于B.txt 中(B.txt 可能还有更多,这没关系)。

问题是两个文件中的行可以按任何顺序排列,所以我想我会在O(nlogn) 中的两个文件中按特定顺序对它们进行排序,然后将A.txt 中的每一行匹配到下一行B.txt 中的行 O(n)。我可以实现一个哈希,但是文件很大,并且这种比较只发生一次,之后这些文件被重新生成,所以我认为这不是一个好主意。

在 Perl 中对文件进行排序的最佳方法是什么?任何排序都可以,只需要 一些 排序。

例如,在字典排序中,这将是

processa,id1=123,id2=5321
processa,id1=213,id2=1
processa,id1=3,id2=521
processa,id1=432,id2=3721
processb,id1=9822,id2=521
processc,id1=822,id2=521

正如我之前提到的,任何排序都可以,只要 Perl 做的很快。

我想在 Perl 代码中这样做,像这样打开文件后

open (FH, "<A.txt");

任何 cmets、想法等都会有所帮助。

【问题讨论】:

  • 如果您正在处理 10,000 行,并且您打算多次使用它——至少我会使用 SQL Lite。
  • 哈希和排序都要求您将整个A.txt 文件加载到内存中。为什么你认为排序比使用哈希更好?
  • @mobrule:我认为 Perl 可能有一些智能的排序方式,因为它是一种为处理文本而构建的语言。某种方式比自己实现哈希更好。从下面的答案看来,散列毕竟是个好主意!
  • 只要你能在内存中处理它就可以了。

标签: perl scripting sorting


【解决方案1】:

要在脚本中对文件进行排序,您仍然需要将整个文件加载到内存中。如果您这样做,我不确定将其排序与仅将其加载到哈希中的优势是什么?

这样的事情会起作用:

my %seen;
open(A, "<A.txt") or die "Can't read A: $!";
while (<A>) {
    $seen{$_}=1;
}
close A;

open(B, "<B.txt") or die "Can't read B: $!";
while(<B>) {
  delete $seen{$_};
}
close B;

print "Lines found in A, missing in B:\n";
join "\n", keys %seen;

【讨论】:

  • 不必一次将整个文件加载到内存中以便对其进行排序。这就是发明归并排序的原因。但如果你有足够的内存,这是一个很好的方法。
【解决方案2】:

这是另一种方法。我们的想法是创建一个灵活的数据结构,让您可以使用grep 轻松回答多种问题。

use strict;
use warnings;

my ($fileA, $fileB) = @ARGV;

# Load all lines: $h{LINE}{FILE_NAME} = TALLY
my %h;
$h{$_}{$ARGV} ++ while <>;

# Do whatever you need.
my @all_lines = keys %h;
my @in_both   = grep {     keys %{$h{$_}} == 2       } keys %h;
my @in_A      = grep {     exists $h{$_}{$fileA}     } keys %h;
my @only_in_A = grep { not exists $h{$_}{$fileB}     } @in_A;
my @in_A_mult = grep {            $h{$_}{$fileA} > 1 } @in_A;

【讨论】:

  • 如果给定的行在一个文件中出现两次但在另一个文件中出现为零怎么办?
  • @HerbN 它工作正常。 %{$h{LINE}} 中的 N 个键告诉您特定 LINE 出现在多少个文件中。并且计数是特定于文件的:每个计数告诉您特定 LINE 在特定 FILE 中出现的 N 次。
【解决方案3】:

嗯,我经常使用 Perl 解析非常大 (600MB) 的每日 Apache 日志文件,并使用哈希来存储信息。我还在一个脚本实例中使用相同的哈希浏览了大约 30 个这些文件。假设您有足够的 RAM,这没什么大不了的。

【讨论】:

    【解决方案4】:

    请问您为什么必须在本地 Perl 中执行此操作?如果调用系统调用或 3 的成本不是问题(例如,您不经常执行此操作且不处于紧密循环中),为什么不简单地这样做:

    my $cmd = "sort $file1 > $file1.sorted";
    $cmd .= "; sort $file2 > $file2.sorted";
    $cmd .= "; comm -23 $file1.sorted $file2.sorted |wc -l";
    my $count = `$cmd`;
    $count =~ s/\s+//g;
    if ($count != 0) {
        print "Stuff in A exists that aren't in B\n";
    }
    

    请注意,comm 参数可能会有所不同,具体取决于您想要什么。

    【讨论】:

    • 这个比较只是我的 Perl 脚本的一小部分。这些文件被生成、比较和处理。至于我为什么要在 Perl 中做所有事情,我需要东西是可移植的,并且可以在各种环境中工作,而不仅仅是 Unix。
    • 嗯,这两个(sort 和 comm,可能还有 wc)在 Windows 上也应该可用,但是是的,这显然不那么便携,你是对的。然而,它更适合大文件。如果您需要纯粹的 Perl 可移植性,哈希解决方案就是您的选择。
    【解决方案5】:

    像往常一样,CPAN 对此有答案。 Sort::ExternalFile::Sort 看起来都可以。我从来没有机会尝试过,所以我不知道哪个更适合你。

    另一种可能性是使用AnyDBM_File 创建一个可能超出可用内存的基于磁盘的哈希。如果不尝试,我不能说使用 DBM 文件会比排序快还是慢,但代码可能会更简单。

    【讨论】:

      【解决方案6】:

      测试A.txt 是否是B.txt 的子集

      open FILE.B, "B.txt";
      open FILE.A, "A.txt";
      
      my %bFile;
      
      while(<FILE.B>) {
         ($process, $id1, $id2) = split /,/;
         $bFile{$process}{$id1}{$id2}++;
      }
      
      $missingRows = 0;
      
      while(<FILE.A>) {
         $missingRows++ unless $bFile{$process}{$id1}{$id2};
         # If we've seen a given entry already don't add it
         next if $missingRows; # One miss means they aren't all verified
      }
      
      $is_Atxt_Subset_Btxt = $missingRows?FALSE:TRUE;
      

      这将为您测试 A 中的所有行是否在 B 中,只读取所有 B,然后在读取 A 时测试数组的每个成员。

      【讨论】:

      • 不,我不会在文件中添加或删除任何信息。我只是重新排列已经存在的东西。这个想法是检查A.txt 是否是B.txt 的子集。
      • @Lazer:更新就是为了做到这一点。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-18
      • 1970-01-01
      • 2019-09-30
      • 1970-01-01
      • 2013-03-16
      • 2021-11-23
      相关资源
      最近更新 更多