这里有两种基于数据描述来减少硬编码特定内容的方法:一种是通过读取那些空字节(然后再转换为换行符),另一种是通过使用nuls解压缩行。
将
$/
变量设置为空字节,然后读取前4(四)个这样的“行”。你可以在这里获取用户ID,然后最后读取的这样一个“行”是其后接的项数。恢复
$/
为换行符并使用正常的
readline
(也称为
<>
)读取该列表。如果此模式确实重复,则重复上述步骤。
use warnings;
use strict;
use feature 'say';
my $file = shift or die "Usage: $0 file\n";
open my $fh, '<', $file or die "Can't open $file: $!";
my ($user_id, $num_items);
while (not eof $fh) {
READ_BY_NUL: {
my $num_of_nul_lines = 4;
local $/ = "\x00";
my $line;
for my $i (1..$num_of_nul_lines) {
$line = readline $fh;
chop $line;
if ($i == 2) {
$user_id = $line;
}
}
$num_items = $line;
}
say "Got: user-id = |$user_id|, and number-of-items = |$num_items|";
my @items;
for (1..$num_items) {
my $line = readline $fh;
chomp $line;
push @items, $line;
}
say for @items;
};
由于在READ_BY_NUL
块中使用local设置了$/
,因此它的先前值将在该块之外恢复。
输出结果如预期,但请添加检查。此外,人们可以想象出现一些错误是有道理的(例如:实际项目数低于给定数量)。
整个过程都在一个while
循环中,并使用eof进行手动检查(和终止),假设模式four-nuls + number-of-lines确实重复(从问题中有点不清楚)。
我使用一个文件进行测试
perl -wE'say "toss\x00user-id\x00this-too\x003\x00item-1\nitem2\nitem 3"'
> a_file_with_nuls.txt
接着将其多次添加以生成 while
循环所需的内容。
最后,在需要的系统上使其读取为 <:raw
,并根据需要进行 unpack
。请参见下文。
如问题所述,(某些?)数据是二进制格式,因此上面所读取的内容需要进行 upack
处理。这也意味着读取可能会遇到空字节的问题——那个数据最初是如何写入的?可以将这些定长字段的未填充部分正好用 nul 填充。
另一种选择是简单地读取行,并解包第一行(然后在读取指定数量的行后每次 unpack
一行,指定为“items”)。
open my $fh, '<:raw', $file or die "Can't open $file: $!";
my @items;
my $block_lines = 1;
while (my $line = <$fh>) {
chomp $line;
if ( $. % $block_lines == 0 ) {
my ($uid, $num_items) = unpack "x8 A7x x13 i3x", $line;
say "User-id: $uid, read $num_items lines for items";
$block_lines += 1 + $num_items;
}
else {
push @items, $line;
}
}
say for @items;
这里要跳过的字节数(x8
和x13
)包括零。
假设每个“块”中读取的“项”(行)数可能不同,并且在读取时将它们加起来(加上空行,以获取总运行$block_lines
),因此可以检查何时再次到达具有空值的行($. % $block_lines == 0
)。
对于未指定的事物,它做出了一些其他(合理的)假设。 这仅经过轻微检查,使用了一些虚构数据。
unpack
需要一个字符串来解包,它不能直接从文件中读取。因此,你需要先将文件读入一个字符串中,但这可能与你所拥有的不太高效。 - Håkon Hæglandreadline
(又称<>
),因为它们都以换行符结尾。我认为没有理由不能混合使用read
和<>
(只是不能使用未缓冲的sysread
)。 - zdimreadline
,你可以测试每行读取的内容是否为一个以 null 结尾的 7 字节字符串;根据描述,似乎没有任何 "项" 可以这样。 - zdimlocal $/
设置为 nul,然后读取四个这样的“行”(丢弃--用户ID--丢弃--要跟随的项目数);然后将$/
改回换行符,并使用刚刚读取的最后一个(项目数)读取相应数量的行(项目)。重复? - zdim