我正在使用perl读取二进制文件。在文件的标题中,有4个字节表示MS-DOS时间。我怎么读这个时间?我不熟悉这种格式。
我发现这是为了参考:http://www.vsft.com/hal/dostime.htm但我仍然不确定如何阅读它。
答案 0 :(得分:2)
您不能使用pack,因为它总是希望从字节边界开始。其中一些值也跨越字节边界,因此您不希望处理单个字节(尽管单词可行)。只是掩盖和转移更容易。
在这个例子中,我设置了掩码,所以我不必过于考虑它,然后使用它们从字符串中获取值。我对DOS时间格式一无所知,但从我读过的内容来看,你必须将秒乘以2(注意它只有5位):
use 5.010;
use strict;
use warnings;
use Data::Dumper;
# seconds minutes hours day month years from 1980
# 5 bits 6 5 5 4 7
my $datetime = 0b11011_000011_11111_01100_1011_0001000;
my $parsed = parse( $datetime );
print Dumper( $parsed );
sub parse {
my( $datetime ) = @_;
state $masks = make_masks();
my %this = map {
$_, ( $datetime & $masks->{$_}[0] ) >> $masks->{$_}[1]
} keys %$masks;
$this{seconds} *= 2;
$this{years} += 1980;
return \%this;
}
sub make_masks {
my %masks = (
seconds => [ 0b11111, 27 ],
minutes => [ 0b111111, 21 ],
hours => [ 0b11111, 16 ],
day => [ 0b11111, 11 ],
month => [ 0b1111, 7 ],
years => [ 0b1111111, 0 ],
);
foreach my $key ( sort { $masks{$a}[1] <=> $masks{$b}[1] } keys %masks ) {
$masks{$key}[0] <<= $masks{$key}[1];
}
return \%masks;
}
我的输出只是一个哈希:
$VAR1 = {
'seconds' => 54,
'hours' => 31,
'years' => 1988,
'month' => 11,
'minutes' => 3,
'day' => 12
};
答案 1 :(得分:2)
另一种方法:
sub mst {
my $msdos_time = shift;
my @t = map { ord }
map { pack("b*", $_) }
map { reverse($_) }
unpack("A5 A6 A5 A5 A4 A7", unpack("b*", $msdos_time));
my %d;
@d{seconds,minutes,hours,day,month,year} = @t;
$d{seconds} *= 2;
$d{year} += 1980;
return \%d;
}
如果$msdos_time
以小端格式表示(我相信)它将如何在内存中布局,这将有效。
(显然链接的map
- s可以合并 - 我这样写它是为了让它更容易看到发生了什么。)
示例:
print Dumper(mst("\x22\x12\x01\x41"));
# byte 0 | byte 1 | byte 2 | byte 3
# 76543210 | 76543210 | 76543210 | 76543210
# S...s seconds
# ..m M.. minutes
# H...h hours
# D...d day
# ..m M month
# Y.....y year
# 00100010 | 00010010 | 00000001 | 01000001
$VAR1 = {
'seconds' => 4,
'hours' => 2,
'month' => 8,
'day' => 1,
'minutes' => 17,
'year' => 2012
};