Как мне разобрать время MS-DOS в perl? - PullRequest
0 голосов
/ 20 августа 2011

Я читаю двоичный файл, используя perl. В заголовках файла есть 4 байта, которые представляют время MS-DOS. Как я читаю это время? Я не знаком с этим форматом.

Я нашел это для справки: http://www.vsft.com/hal/dostime.htm, но я все еще не уверен, как это прочитать.

Ответы [ 2 ]

2 голосов
/ 21 августа 2011

Другой подход:

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 -с могут быть объединены - я написал это таким образом, чтобы было легче увидеть, что происходит.)

Пример:

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
    };
2 голосов
/ 20 августа 2011

Вы не можете использовать pack , потому что он всегда хочет начать с границы байта.Некоторые из этих значений также выходят за границы байтов, поэтому вы не хотите иметь дело с отдельными байтами (хотя слова будут работать).Проще всего замаскировать и сдвинуть.

В этом примере я настроил маски, чтобы мне не пришлось слишком задумываться об этом, а затем использую их для извлечения значений из строки.Я действительно ничего не знаю о формате времени DOS, но из того, что я прочитал, вы должны умножить секунды на 2 (обратите внимание, что это только пять бит):

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
        };
...