比较文件和报告统计信息中的记录 - 场景1

时间:2009-05-20 23:06:36

标签: perl file comparison visual-diff

要求是:

事实1:我们有遗留系统生成的一些数据文件

事实2:我们有一些新系统生成的数据文件应该最终取代传统的

事实3:

  1. 这两个文件都是文本/ ASCII文件, 记录由组成 多行。
  2. 记录中的每一行都包含在内 of fieldname和fieldvalue。
  3. 这些行的格式 提出的是1之间的不同 和2,但是fieldname和fieldvalue 可以从每一行中提取 通过使用正则表达式
  4. 字段名称可以在1和之间更改 2,但我们有一个映射 与他们联系
  5. 每条记录都有唯一的标识符 这有助于我们将遗产联系起来 记录新订单作为订购 输出文件中的记录需要 两个系统都不一样。
  6. 要比较的每个文件至少为 10 MB到平均情况30 - 35 MB
  7. 事实4:当我们迭代构建新系统时,我们需要在完全相同的条件下比较两个系统生成的文件并协调差异。

    事实5:这种比较是使用昂贵的视觉差异工具手动完成的。为了解决这个问题,我编写了一个工具,将两个不同的字段名称放在一个通用名称中,然后在每个文件中对每个记录中的字段名称进行排序,以便它们按顺序同步(新文件可以有多余的字段在视觉差异

    事实6:由于比较是由人类手工完成的,而人类犯错误,我们会得到虚假的姿势和负面因素,这会严重影响我们的时间表。

    显然问题是,“ALG”和“DS”应该是什么?

    我必须解决的方案:

    人们继续在视觉上检查差异 - 在这里,现有脚本的性能令人沮丧 - 大多数处理似乎是按字典顺序对行数组进行排序(读取/获取数组元素:Tie :: File :: FETCH,Tie :: File :: Cache :: lookup并将其放入正确的位置,以便排序:Tie :: File :: Cache :: insert,Tie :: File :: Heap :: insert)< / p>

    use strict;
    use warnings;
    
    use Tie::File;
    
    use Data::Dumper;
    
    use Digest::MD5 qw(md5_hex);
    
    # open an existing file in read-only mode
    use Fcntl 'O_RDONLY';
    
    die "Usage: $0 <unsorted input filename> <sorted output filename>" if ($#ARGV < 1);
    
    our $recordsWrittenCount = 0;
    our $fieldsSorted = 0;
    
    our @array;
    
    tie @array, 'Tie::File', $ARGV[0], memory => 50_000_000, mode => O_RDONLY or die "Cannot open $ARGV[0]: $!";
    
    open(OUTFILE, ">" .  $ARGV[1]) or die "Cannot open $ARGV[1]: $!";
    
    our @tempRecordStorage = ();
    
    our $dx = 0;
    
    # Now read in the EL6 file
    
    our $numberOfLines = @array; # accessing @array in a loop might be expensive as it is tied?? 
    
    for($dx = 0; $dx < $numberOfLines; ++$dx)
    {
        if($array[$dx] eq 'RECORD')
        {
            ++$recordsWrittenCount;
    
            my $endOfRecord = $dx;
    
            until($array[++$endOfRecord] eq '.')
            {
                push @tempRecordStorage, $array[$endOfRecord];
                ++$fieldsSorted;
            }
    
            print OUTFILE "RECORD\n";
    
            local $, = "\n";
            print OUTFILE sort @tempRecordStorage;
            @tempRecordStorage = ();
    
            print OUTFILE "\n.\n"; # PERL does not postfix trailing separator after the last array element, so we need to do this ourselves)
    
            $dx = $endOfRecord;     
        }
    }
    
    close(OUTFILE);
    
    # Display results to user
    
    print "\n[*] Done: " . $fieldsSorted . " fields sorted from " . $recordsWrittenCount . " records written.\n";
    

    所以我想到了它,我相信,如果是trie,也许是后缀trie / PATRICIA trie,那么在插入时,每个记录中的字段都会被排序。 因此,我不必一次性对最终数组进行排序,费用将摊销(我的推测)

    在这种情况下出现另一个问题 - Tie :: File使用数组来抽象文件中的行 - 将行读入树中然后将它们序列化为数组将需要额外的内存和处理/

    问题是 - 这会花费多于当前对绑定数组进行排序的成本吗?

1 个答案:

答案 0 :(得分:2)

Tie :: File非常慢。这有两个原因:首先,绑定变量明显慢于标准变量。另一个原因是,在Tie :: File的情况下,数组中的数据是在磁盘上而不是在内存中。这大大减慢了访问速度。 Tie :: File的缓存可以在某些情况下帮助提高性能,但是当你像在这里一样只循环数组一个元素时就不行了。 (只有在重新访问相同的索引时,缓存才有用。)使用Tie :: File的时候就是当你有一个算法需要同时拥有内存中的所有数据但你没有足够的内存来执行此操作时。由于你只使用Tie :: File一次处理一行文件不仅没有意义,因此它是有害的。

我不认为特里在这里是正确的选择。我会使用简单的HoH(散列哈希)代替。您的文件足够小,您应该能够立即获取内存中的所有内容。我建议解析每个文件并构建如下所示的哈希:

%data = (
  id1 => {
    field1 => value1,
    field2 => value2,
  },
  id2 => {
    field1 => value1,
    field2 => value2,
  },
);

如果在构建数据结构时使用映射来规范化字段名称,则可以使比较更容易。

要比较数据,请执行以下操作:

  1. 执行两个哈希键的设置比较。这应该生成三个列表:仅存在于旧数据中的ID,仅存在于新数据中的ID以及存在于两者中的ID。
  2. 报告仅出现在一个数据集中的ID列表。这些是在其他数据集中没有相应记录的记录。
  3. 对于两个数据集中的ID,请按字段比较每个ID字段的数据并报告任何差异。