challenge 固定長データ

固定長のデータが記載されたファイルを読み込むプログラムを作成してください。読み込んだデータは、複数の値を格納できるデータ型に格納してください。

ファイルには、すべて ascii 文字で以下のデータが格納されています。デリミタはなく、固定長で格納されています。レコードとレコードのあいだも改行はありません。

  1. 姓 (12文字) 文字数が足りない場合は、後ろを空白で埋めてあります。
  2. 名 (12文字) 文字数が足りない場合は、後ろを空白で埋めてあります。
  3. 性別 (F,M,Uの3種類、1文字)
  4. 年齢 (3桁の数字、桁数が足りない場合は、ゼロで埋めず、頭を空白で埋めてあります。
  5. 年 2008 固定
  6. 月 03 固定
  7. さらに以下のデータが日付分くりかえされます。
    1. 日付 (01 〜 31) 2文字
    2. 朝食のメニュー (500文字)
    3. 昼食のメニュー (500文字)
    4. 夕食のメニュー (500文字)

以上の形式のデータ500人分を読みこんで、データを複数の値を格納できるデータ型に格納してください。データに大して何か処理を行う必要はなく、すぐに破棄してかまいません。

この問題は、このようなファイルをどのように扱うかを知りたくて作成しました。

Posted feedbacks - Perl

やっつけですがunpackを使った例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/perl

use strict;
use warnings;

my $dat;
if (@ARGV) {
  open $dat, shift or die;
} else {
  $dat = \*STDIN;
}

my $bytes;
my @records;

for (1..500) {
  read $dat, $bytes, 12 + 12 + 1 + 3 + 4 + 2;
  my ($lname, $fname, $sex, $age, $year, $month) = unpack "A12A12A1A3A4A2", $bytes;
  $lname =~ s/\s*$//;
  $fname =~ s/\s*$//;
  my %record = (last_name => $lname,
             first_name => $fname,
         sex => $sex,
         age => $age + 0,
         year => $year + 0,
         month => $month + 0,
         menues => []);
  for (1..31) {
    read $dat, $bytes, 2 + 500 + 500 + 500;
    my ($day, $breakfast, $lunch, $dinner) = unpack "A2A500A500A500", $bytes;
    $breakfast =~ s/^\s*|\s*$//;
    $lunch =~ s/^\s*|\s*$//;
    $dinner =~ s/^\s*|\s*$//;
    push @{$record{menues}}, {day => $day + 0,
                   breakfast => $breakfast,
                   lunch => $lunch,
                   dinner => $dinner};
  }
  push @records, \%record;
}

#テスト出力
use Data::Dumper;
print Dumper @records;

ついでにテストデータ生成スクリプトを貼っておきます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/perl

use strict;
use warnings;

my @lnames = map { sprintf "%-12s", $_ } qw/Foo Bar Baz Jim Bob Beth Cathy/;
my @fnames =
  map { sprintf "%-12s", $_ } qw/Hoge Fuga Piyo Jacson Page McDonald Hamond/;
my @sexes = qw/F M U/;
my @ages = map { sprintf "%03d", $_ } 0 .. 999;
my @dishes =
  map { sprintf "%-500s", $_ }
  qw/BaconEgg Coffee HamEgg BoiledEgg EggInBasket Udon Steak
  SunnySideUp Onigiri Ramen/;

sub randomint { int rand shift }

for ( 1 .. 500 ) {
    print $lnames[ randomint( scalar @fnames ) ],
      $fnames[ randomint( scalar @lnames ) ],
      $sexes[ randomint( scalar @sexes ) ], $ages[ randomint( scalar @ages ) ],
      "200803";
    for my $day ( 1 .. 31 ) {
        $day = sprintf "%02d", $day;
        print $day, $dishes[ randomint( scalar @dishes ) ],
          $dishes[ randomint( scalar @dishes ) ],
          $dishes[ randomint( scalar @dishes ) ];
    }
}

どうも encoding まわりの扱いに自信がありません.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
use strict;
use warnings;
use Data::Dumper;
use constant ROWSIZE => 1536;
no encoding;
my @cols = qw( family_name four_name gender age year mon date
               breakfast lunch dinner );
my $i   = 0;
my @row = ();
print '(';
while(!eof STDIN){
  my ($buf, %rec) = ("",());
  read  STDIN , $buf , ROWSIZE;
  printf "do{%s}," ,  Dumper { map{
    s/^\s+//; s/\s+$//;
    ( $cols[ $i++ % @cols] , $_ );
  } unpack "A12A12A1A3A4A2A2A500A500A500" , $buf };
}
print ')';

Index

Feed

Other

Link

Pathtraq

loading...