【问题标题】:File parsing in perl reading first and last linesperl中的文件解析读取第一行和最后一行
【发布时间】:2017-01-09 17:13:45
【问题描述】:

我有一个第一行的文件

=== Verbose logging started: 1/3/2017  17:41:55  Build type: SHIP UNICODE 5.00.7601.00  Calling process: C:\Windows\SysWOW64\msiexec.exe ===

最后一行

=== Verbose logging stopped: 1/3/2017  17:49:17 ===

我对那些行(17:41:5517:49:17)中的时间字段感兴趣,想找出从开始到停止的时间差。

我尝试读取数组中的文件并获取第一行和最后一行

my $last = pop (@array);
my $first = shift (@array);

但是在数组中获取时间字段变得越来越困难。

您能建议任何替代方法吗?

【问题讨论】:

标签: perl


【解决方案1】:

如果您想读取可能非常大的日志文件的第一行和最后一行,则不应将其全部放入数组中,因为它可能会消耗大量内存。相反,只需阅读第一行和最后一行。

你可以很容易地阅读第一行。

 use v5.10;
 use strict;
 use warnings;
 use autodie;

 open my $fh, $logfile;
 my $first = <$fh>;

您可以使用seek 跳转到文件末尾,然后使用read 分块向后读取,直到获得整行。这可能会变得复杂。幸运的是,有 File::ReadBackwards 可以为您完成这项工作。

use Carp;
use File::ReadBackwards;

my $backwards = File::ReadBackwards->new( $logfile )
    or croak "Can't open $logfile: $!";
my $last = $backwards->readline;

请注意,如果文件末尾有任何杂散的换行符,那将是最后一行,因此您可能需要继续阅读,直到找到所需的内容。

# Read lines backwards until we get something that
# contains non-whitespace.
while( my $last = $backwards->readline ) {
    last if $last =~ /\S+/;
}

这是获取第一行和最后一行的更简单但更慢(对于大文件)的方法。像以前一样阅读第一行,然后阅读每一行,但只保留最后一行。

my $last;
while( my $line = <$fh> ) { $last = $line }

它仍然需要读取整个文件,但它只保留最后一个在内存中。


一旦你有了它,你就可以解析该行并将其转换为 Time::Piece 对象以便更轻松地使用它。

# === Verbose logging started: 1/3/2017  17:41:55 ... ===
# === Verbose logging stopped: 1/3/2017  17:49:17 ===
sub log_time {
    my $line = shift;

    # This captures the 1/3/2017  17:49:17 part
    my($datetime) = $line =~
        /^=== Verbose logging (?:started|stopped):\s*(\d+/\d+/\d+\s+\d+:\d+:\d+)/;

    # Parse it into a Time::Piece object.
    return Time::Piece->strptime($datetime, "%m/%d/%Y %H:%M:%S");
}

strptime 是许多语言用来解析日期(字符串解析时间)的函数。 strftime(字符串格式时间)用于格式化日期。他们共享相同的迷你语言。查看strftime 文档以了解那里发生了什么。

一旦你有了,you can get the difference in seconds by subtracting them

my $start = log_time($first);
my $end   = log_time($last);

say "Seconds elapsed: ".$end - $start;

【讨论】:

    【解决方案2】:

    我对 Schwern 有一种稍微不那么复杂的方法,那就是使用 Unix 命令:

    #!/usr/bin/perl
    
    use strict;
    use English;
    
    my $first=`head -1 $ARGV[0]`;
    my $last=`tail -1 $ARGV[0]`;
    
    print "$first\n";
    print "$last\n";
    

    【讨论】:

    • 我相信tail -1 会从文件末尾读取,所以它避免读取整个文件。不要使用原型;它们不是函数签名,它们用于非常专业的目的。如果您想要函数签名,请在较新的 Perls 中使用 Method::Signaturesuse feature 'signatures' 之类的东西。最后,没有必要写command()backticks 会为你做这些。 my $first = `head -1 $ARGV[0]`
    • @Schwern 谢谢,我想我已经坚持了 15 年。感谢您更新我的答案。
    • 你为什么有use English?你实际上并没有使用任何变量名。
    • @simbabque 习惯,我同意没有必要。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-03
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多