您可以从 Perl 中的 .tar.bz2 存档逐文件逐行流式传输吗?
Can you stream file-per-file, line-per-line from a .tar.bz2 archive in Perl?
我们有很多压缩数据,它们实际上是目录及其子目录的压缩磁带存档,其中包含 XML 个文件;例如
omega/
- alpha/
- a/
- file1.xml
- file2.xml
- file3.xml
- b/
- file1.xml
- file2.xml
- file3.xml
- c/
- ...
- beta/
- a/
- file1.xml
- file2.xml
- file3.xml
- b/
- ...
- c/
- ...
- gamma/
- a/
- ...
- b/
- ...
- c/
- ...
结果将是 omega.tar.bz2
这样的文件,这些文件的大小可达数百 GB。
即使我们知道这是一个 archive 文件类型,但如果我们仍然能够在需要时使用其内容,那就太好了。因此,我想知道是否有可能以流式方式从 Perl 中读取这些文件,即无需首先解压和解压缩磁盘上的所有内容,或者无需加载 whole *.tar.bz2
文件存入内存。
我知道使用 IO::Uncompress
可以使用 Bunzip2,但据我所知和测试,这会将整个文件读入内存,这对我们的大文件来说是不可能的。下面关于 Bunzipping 的示例代码(不包括 TAR)。
use strict;
use warnings;
use IO::Uncompress::Bunzip2 qw(bunzip2 $Bunzip2Error) ;
my $filename = '/path/to/file/file1.xml.bz2';
open(my $fh, '<', $filename)
or die "Could not open file '$filename' $!";
my $buffer ;
bunzip2 $filename => $buffer
or die "bunzip2 failed: $Bunzip2Error\n";
print STDOUT "$buffer\n";
考虑到 TAR,还有 Archive::Extract
模块允许将 .tar.bz2
文件(类型 tbz
)读入 Extract Object
, 但这又会将整个文件读入内存,这对于我们巨大的文件来说是不可能的。
由于我自己对该主题的研究,我认为不太可能以流式方式(即一行一行)读取 TAR 的 BZIP2。不过,我没有压缩经验,所以也许有一种方法可以在给定多个数据块的情况下重建文件行。
Tl;dr:您可以从 BZIP2 压缩的 TAR 存档流式传输文件内容(逐行或类似内容)吗?
有 Compress::Raw::Bzip2 which allows you to decompress bzip2 input chunk by chunk, i.e. in a stream. But since .tar.bz2 is first a tar file which is then compressed with bzip2 you would need to first decompress all data up to the files location in the tar file before you have access to the data you want, i.e. there is no way to seek to the file without decompression everything up to this file. If you are fine with this you might be able to use Archive::Tar::Stream,即将来自 bzip2 解码器的输入馈送到流式 Tar 解析器。我自己从未使用过它,但看起来它就是为这种用例开发的。
如果您可以选择更改输入文件的格式,我建议您使用将压缩文件存储在存档中的格式(就像 ZIP 那样),而不是压缩整个存档(即 .tar.bz2).这样你就可以很容易地找到一个特定的压缩文件并只解压这个而不是这个文件之前的所有内容。
所有 IO::Compress 和 IO::Uncompress 模块都支持流式传输,包括 IO::Uncompress::Bunzip2。您展示的示例代码(见下文)使用了一种便捷方法 (bunzip2),用于您希望从文件中读取所有压缩数据并将其一次性解压缩到缓冲区的常见用例。
my $buffer ;
bunzip2 $filename => $buffer
or die "bunzip2 failed: $Bunzip2Error\n";
这是流式 Bunzip2 用例的用法
my $bz = IO::Uncompress::Bunzip2->new($filename);
# $bz is a regular Perl filehandle, so can read it a line at a time
while (<$bz>)
{
....
}
# or a bock at a time
read($bz, $buffer, 1024);
close $gz;
如果你能找到一个 tar 模块接受 perl 文件句柄并且它本身是流式的,你可以给它一个 IO::Uncompress::Bubzip2 对象。
另一种选择是让“real”tar 二进制文件为您处理。较新版本的 gnu tar 将自动检测压缩,您可以获得 tar 以写入标准输出。所以你可以打开 tar 命令的文件句柄,像这样
open my $data, "tar -Of $file.tar.bz2 |";
while (<$data>)
{
....
}
我们有很多压缩数据,它们实际上是目录及其子目录的压缩磁带存档,其中包含 XML 个文件;例如
omega/
- alpha/
- a/
- file1.xml
- file2.xml
- file3.xml
- b/
- file1.xml
- file2.xml
- file3.xml
- c/
- ...
- beta/
- a/
- file1.xml
- file2.xml
- file3.xml
- b/
- ...
- c/
- ...
- gamma/
- a/
- ...
- b/
- ...
- c/
- ...
结果将是 omega.tar.bz2
这样的文件,这些文件的大小可达数百 GB。
即使我们知道这是一个 archive 文件类型,但如果我们仍然能够在需要时使用其内容,那就太好了。因此,我想知道是否有可能以流式方式从 Perl 中读取这些文件,即无需首先解压和解压缩磁盘上的所有内容,或者无需加载 whole *.tar.bz2
文件存入内存。
我知道使用 IO::Uncompress
可以使用 Bunzip2,但据我所知和测试,这会将整个文件读入内存,这对我们的大文件来说是不可能的。下面关于 Bunzipping 的示例代码(不包括 TAR)。
use strict;
use warnings;
use IO::Uncompress::Bunzip2 qw(bunzip2 $Bunzip2Error) ;
my $filename = '/path/to/file/file1.xml.bz2';
open(my $fh, '<', $filename)
or die "Could not open file '$filename' $!";
my $buffer ;
bunzip2 $filename => $buffer
or die "bunzip2 failed: $Bunzip2Error\n";
print STDOUT "$buffer\n";
考虑到 TAR,还有 Archive::Extract
模块允许将 .tar.bz2
文件(类型 tbz
)读入 Extract Object
, 但这又会将整个文件读入内存,这对于我们巨大的文件来说是不可能的。
由于我自己对该主题的研究,我认为不太可能以流式方式(即一行一行)读取 TAR 的 BZIP2。不过,我没有压缩经验,所以也许有一种方法可以在给定多个数据块的情况下重建文件行。
Tl;dr:您可以从 BZIP2 压缩的 TAR 存档流式传输文件内容(逐行或类似内容)吗?
有 Compress::Raw::Bzip2 which allows you to decompress bzip2 input chunk by chunk, i.e. in a stream. But since .tar.bz2 is first a tar file which is then compressed with bzip2 you would need to first decompress all data up to the files location in the tar file before you have access to the data you want, i.e. there is no way to seek to the file without decompression everything up to this file. If you are fine with this you might be able to use Archive::Tar::Stream,即将来自 bzip2 解码器的输入馈送到流式 Tar 解析器。我自己从未使用过它,但看起来它就是为这种用例开发的。
如果您可以选择更改输入文件的格式,我建议您使用将压缩文件存储在存档中的格式(就像 ZIP 那样),而不是压缩整个存档(即 .tar.bz2).这样你就可以很容易地找到一个特定的压缩文件并只解压这个而不是这个文件之前的所有内容。
所有 IO::Compress 和 IO::Uncompress 模块都支持流式传输,包括 IO::Uncompress::Bunzip2。您展示的示例代码(见下文)使用了一种便捷方法 (bunzip2),用于您希望从文件中读取所有压缩数据并将其一次性解压缩到缓冲区的常见用例。
my $buffer ;
bunzip2 $filename => $buffer
or die "bunzip2 failed: $Bunzip2Error\n";
这是流式 Bunzip2 用例的用法
my $bz = IO::Uncompress::Bunzip2->new($filename);
# $bz is a regular Perl filehandle, so can read it a line at a time
while (<$bz>)
{
....
}
# or a bock at a time
read($bz, $buffer, 1024);
close $gz;
如果你能找到一个 tar 模块接受 perl 文件句柄并且它本身是流式的,你可以给它一个 IO::Uncompress::Bubzip2 对象。
另一种选择是让“real”tar 二进制文件为您处理。较新版本的 gnu tar 将自动检测压缩,您可以获得 tar 以写入标准输出。所以你可以打开 tar 命令的文件句柄,像这样
open my $data, "tar -Of $file.tar.bz2 |";
while (<$data>)
{
....
}