【问题标题】:Perl: How do I get "bytes read" from md5::digest addfile()?Perl:如何从 md5::digest addfile() 获取“字节读取”?
【发布时间】:2019-04-16 17:28:01
【问题描述】:

我正在使用 Digest::MD5 来计算数据流的 MD5;即一个 GZIPped 文件(或者准确地说,3000),它太大而无法放入 RAM。所以我这样做:

 use Digest::MD5 qw(md5_base64);

 my ($filename) = @_;                # this is in a sub
 my $ctx = Digest::MD5 -> new;

 $openme = $filename;        # Usually, it's a plain file
 $openme = "gunzip -c '$filename' |" if ($filename =~ /\.gz$/); # is gz

 open (FILE, $openme); # gunzip to STDOUT
 binmode(FILE);
 $ctx -> addfile(*FILE);   # passing filehandle
 close(FILE);

这是成功的。 addfile 在 gunzip 的输出中巧妙地啜饮并给出正确的 MD5。

但是,我真的非常想知道 slurped 数据的大小(在这种情况下是压缩的“文件”)。

我可以添加一个额外的

  $size = 0 + `gunzip -c very/big-file.gz | wc -c`;

但这将涉及两次读取文件。

有什么方法可以提取从 Digest::MD5 中提取的字节数?我尝试捕获结果:$result = $ctx -> addfile(*FILE); 并在 $result 和 $ctx 上执行 Data::Dumper,但没有出现任何有趣的结果。

编辑:这些文件通常不会被 gzip 压缩。添加代码以显示我的实际工作。

【问题讨论】:

  • 也许使用IO::Uncompress::Gunzip 一次读取文件一个块并计算数据的长度及其摘要,而不是依赖外部程序?

标签: perl md5


【解决方案1】:

我会在 perl 中完成这一切,无需依赖外部程序进行解压:

#!/usr/bin/perl
use warnings;
use strict;
use feature qw/say/;
use IO::Uncompress::Gunzip qw/$GunzipError/;
use Digest::MD5;

my $filename = shift or die "Missing gzip filename!\n";

my $md5 = Digest::MD5->new;
# Allow for reading both gzip format files and uncompressed files.
# This is the default behavior, but might as well be explicit about it.
my $z = IO::Uncompress::Gunzip->new($filename, Transparent => 1)
  or die "Unable to open $filename: $GunzipError\n";
my $len = 0;

while ((my $blen = $z->read(my $block)) > 0) {
  $len += $blen;
  $md5->add($block);
}
die "There was an error reading the file: $GunzipError\n" unless $z->eof;

say "Total uncompressed length: $len";
say "MD5: ", $md5->hexdigest;

如果您想使用gunzip 而不是核心IO::Uncompress::Gunzip 模块,您可以执行类似的操作,不过,使用read 一次获取一大块数据:

#!/usr/bin/perl
use warnings;
use strict;
use autodie; # So we don't have to explicitly check for i/o related errors
use feature qw/say/;
use Digest::MD5;

my $filename = shift or die "Missing gzip filename!\n";

my $md5 = Digest::MD5->new;
# Note use of lexical file handle and safer version of opening a pipe
# from a process that eliminates shell shenanigans. Also uses the :raw
# perlio layer instead of calling binmode on the handle (which has the
# same effect)
open my $z, "-|:raw", "gunzip", "-c", $filename;
# Non-compressed version
# open my $z, "<:raw", $filename;
my $len = 0;

while ((my $blen = read($z, my $block, 4096)) > 0) {
  $len += $blen;
  $md5->add($block);
}

say "Total uncompressed length: $len";
say "MD5: ", $md5->hexdigest;

【讨论】:

  • 有趣。我的部分问题是我不能依赖被 gzip 压缩的文件。就像现在一样,编写代码是为了读取普通文件,我有一个简单的垫片:`if /gz$/ { $filename = "gunzip -c $filename |" } 在打开之前卡住了。
  • @Harper 模块的默认行为是,如果文件不是 gzip 格式,则直接通过原样传递(这可以使用构造函数的 Transparent 选项显式设置;让我更新代码以显示这一点。)
  • @Shawn 这真的很漂亮。 嗯,等一下。该模块如何处理应该是为 gzip 文件但文件损坏的文件?我有一些。
  • @Harper 还添加了一些错误报告,用于在解压缩文件时遇到问题。
【解决方案2】:

您可以自己阅读内容,并将其提供给$ctx-&gt;add($data),并持续计算您通过了多少数据。无论您是在一次调用中添加所有数据,还是在多次调用中添加所有数据,都不会对底层算法产生任何影响。文档包括:

    All these lines will have the same effect on the state of the $md5 object:

        $md5->add("a"); $md5->add("b"); $md5->add("c");
        $md5->add("a")->add("b")->add("c");
        $md5->add("a", "b", "c");
        $md5->add("abc");

这表示您一次只能做一件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-26
    • 1970-01-01
    • 1970-01-01
    • 2011-01-17
    • 1970-01-01
    • 2021-07-08
    • 1970-01-01
    相关资源
    最近更新 更多