Perl 解析文件中的“文件”;错误";,将找到的记录的索引作为键写入新文件。使用密钥文件搜索第一个文件中的记录

Perl 解析文件中的“文件”;错误";,将找到的记录的索引作为键写入新文件。使用密钥文件搜索第一个文件中的记录,perl,file,logging,hash,big-o,Perl,File,Logging,Hash,Big O,[upate:这里有我没有看到的Perl文件操作函数/方法吗?] 我在面试时遇到了一道Perl问题。我相信我给出了问题的答案,但不是他们想要的“脑筋急转弯”的答案。因此,我重新学习了Perl来编写代码,并与他们的答案进行比较测试。我的Perl不是很强,所以我不确定我是否以最佳方式编写了他们的解决方案。我希望对他们公平(我仍然认为我的解决方案的速度是指数级的。) 问题:扫描包含多行日志项的日志文件 01 BEGIN 01 some text 02 BEGIN 01 more Text 01

[upate:这里有我没有看到的Perl文件操作函数/方法吗?]

我在面试时遇到了一道Perl问题。我相信我给出了问题的答案,但不是他们想要的“脑筋急转弯”的答案。因此,我重新学习了Perl来编写代码,并与他们的答案进行比较测试。我的Perl不是很强,所以我不确定我是否以最佳方式编写了他们的解决方案。我希望对他们公平(我仍然认为我的解决方案的速度是指数级的。)

问题:扫描包含多行日志项的日志文件

01 BEGIN 
01 some text 
02 BEGIN 
01 more Text 
01 END 
02 other text 
02 END 
03 BEGIN 
03 even more text 
03 ERROR
...
找到错误记录并将整个记录写入新文件。上述例子将产生:

03 BEGIN 
03 even more text 
03 ERROR
在新文件中。以“end”结尾的日志条目/记录不在新文件中

限制:您不能一次读取整个文件。由于文件大小和内存限制,您必须一次读取一行

他们的解决办法:

他们特别不希望将密钥文件作为约束完全读入内存。 每次出现“错误”时,都要扫描日志文件。如果找到,则将“索引”编号03作为密钥写入新文件。接下来将使用此密钥文件

03
将与日志文件中的索引号匹配的日志文件重新扫描到密钥文件中的密钥

我陷入困境,需要帮助的是如何更有效地解决问题

我一次读取一行中的第一个日志文件,以获取日志条目索引。 然后,我一次读取一行密钥文件以获取密钥。 然后我对索引和键进行了“eq”

    if ( $_[0] eq $key )
    {
        return 1;
    }
在我看来,这是低效的。这将读取整个原始日志文件两次

  • 找到钥匙
  • 将每个日志行与密钥文件中的每个密钥匹配
  • 随着密钥数量的增加,这会对日志文件中的每一行读取密钥文件X次,由于错误率的原因,将一个日志行与多个密钥进行匹配所需的时间会快速增长

    如果有10个键和100行要读取,那么这就是10*100个键文件读取。 如果有100个键和100行要读取(100%错误),则100*100个键文件将读取

    因此,在不将密钥文件读入内存(例如将密钥存储在散列中)的情况下,有没有更有效的方法来将日志文件行与密钥进行匹配

    是否有办法为密钥文件中的每一行“grep”密钥文件? 有更好的办法吗? 如果密钥文件已排序,是否有帮助? 是否有一种文件操作方法可以用来删除/忽略我在日志文件中已经找到的记录的密钥,从而不断地将密钥文件的大小减小到只有我没有找到的密钥

    我可以复制错误日志文件和密钥文件吗?然后从两个文件中删除我知道不需要的行。我可以对密钥文件进行排序。错误日志文件可以排序,但错误是嵌套/重叠的

    我没有看到使用perl进行老式的文件操作吗

    ===== 我的解决方案是一次读取一行日志文件。然后每次看到“开始”时创建一个队列,以存储具有相同索引的每组行。然后将队列放入散列中。日志条目行队列的散列

    my %queues;
    my @logLines; # queue to hold lines in order for later write if needed
    push @logLines, $line;
    
    $queues{ $index } = [@logLines];
    
    如果读取了“END”,那么我就删除了队列,因为我不需要将其写出来

    如果读取了“ERROR”,那么我写出了队列的内容,删除了队列

    然后删除队列的哈希项

    结果是“错误”块以正确的顺序写入文件。 无需写入或重新匹配密钥文件。 无需对最终文件进行排序

    针对10000个日志记录文件(约80000行)

    我认为我的代码会:

    A. Be so much faster
    B. Be the same no matter the error rate.
    
    这里有成年人想计算这些的O(n)吗

    谢谢大家

    随着密钥数量的增加,这会对日志文件中的每一行读取密钥文件X次,由于错误率的原因,将一个日志行与多个密钥进行匹配所需的时间会快速增长

    不,它实际上只需要两次通过。一次识别错误块。将它们保存在散列中。然后,在第二个过程中,检查散列中是否存在密钥,以决定是否保留块

    我猜问题在于需要保留的行数可能是无限的

    以下假设您可以在内存中保留具有
    错误
    状态代码的任务的查找表。如果出现错误的任务数量也可能很大,则可以使用磁盘上的键值存储(例如Berkeley DB、
    memcache
    等)替换简单的哈希查找表:


    如果是可能的话,我会考虑将行插入到SQLite数据库中,并用< /代码>和<>代码>按打印<适当的<代码>组。< /P>重叠<代码>开始和<代码>结束<代码>标记…这是否意味着它们标记了“部分”,但这些部分可以嵌套?如果这些确实是分区,那么任何一个分区可以获得多大的信息?使用哈希确实可以加快速度。但他们的解决方案是不使用%散列。我曾多次明确指出,使用散列可以加快进程。他们反驳说不需要这样做。一次读取一行文件而不保存行是可以的。我猜他们是想做perl1>perl2>perl3的事情。或者他们想在微服务中运行一些不需要散列的东西。在这一点上,我们有一个模糊的问题描述,没有真正的约束规范,所以我觉得进一步的推测是没有用的。最重要的是,代码中存在浪费性的结构,例如,

    $queues{$index}=[@logLines]这让我怀疑你可能误解了他们所说的和/或我所说的和/或我不理解你所说的。我不是在评判。我正在解释为什么你的问题应该以“太宽:Pl”结尾
    
    A. Be so much faster
    B. Be the same no matter the error rate.
    
    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    
    use autouse Carp => 'croak';
    
    run($ARGV[0]);
    
    sub run {
        my $logfile = shift;
    
        my $failed_tasks = identify_failed_tasks($logfile);
        print_failed_tasks($logfile, $failed_tasks);
    }
    
    sub identify_failed_tasks {
        my $logfile = shift;
    
        open my $in, '<', $logfile
            or croak "Failed to open '$logfile' for reading: $!";
    
        my %failed_tasks;
    
        while (my $line = <$in>) {
            last unless $line =~ /\S/;
            my ($id, $status) = split ' ', $line, 3;
            next unless $status eq 'ERROR';
            $failed_tasks{$id} = undef;
        }
    
        close $in
            or croak "Failed to close '$logfile': $!";
    
        return \%failed_tasks;
    }
    
    sub print_failed_tasks {
        my $logfile = shift;
        my $failed_tasks = shift;
    
        open my $in, '<', $logfile
            or croak "Failed to open '$logfile' for reading: $!";
    
        while (my $line = <$in>) {
            last unless $line =~ /\S/;
            my ($id) = ($line =~ /^(\S+)/);
            next unless exists $failed_tasks->{$id};
            print $line;
        }
    
        close $in
            or croak "Failed to close '$logfile': $!";
    
        return;
    }