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 - C

ごく普通に。
しかしC系の人間にしかわからないバウンダリの痛み。
このデータ仕様わざとやってるでしょ?
 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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <stdio.h>
#include <stdlib.h>

typedef struct tagDailyMenu {
    char date[2];
    char breakfast[500];
    char lunch[500];
    char dinnar[500];
} DailyMenu;

typedef struct tagPerson {
    char family[12];
    char name[12];
    char sex;
    char age[3];
    char year[4];
    char month[3];
} Person;

typedef struct tagRecord {
    Person person;
    DailyMenu days[31];
} Record;

int main(void)
{
    FILE *fp;
    char *filename = "data.dat";
    int readlen = 0;
    Record rec;

    fp = fopen(filename, "r");
    if (fp == NULL) {
        printf("ファイルオープン失敗\n");
        return 1;
    }

    memset(&rec, 0x00, sizeof(Record));
    while ((readlen = fread(&rec.person, 34, 1, fp)) == 1) {

        int readlen2 = 0;
        int day = 0;

        for (day = 0; day < 31; day++) {
            int readlen2 = fread(&rec.days[day], 1502, 1, fp);
            if (readlen2 < 1) {
                printf("ファイルがこわれています。\n");
                break;
            }
        }
        if (day < 31) {
            break;
        }
        printf("name: [%.12s %.12s] %.4s/%.2s [%.2s/%.10s/%.10s/%.10s]\n",
            rec.person.family, rec.person.name,
            rec.person.year, rec.person.month,
            rec.days[0].date, rec.days[30].breakfast,
            rec.days[30].lunch, rec.days[30].dinnar
            );

        memset(&rec, 0x00, sizeof(Record));
    }
    fclose(fp);
    return 0;
}

エラー処理をはしょればこんなかんじ? あと、31日固定として。

 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
#include <stdio.h>
#include <stdlib.h>

typedef struct menu_ {
  char date[2];
  char breakfast[500];
  char lunch[500];
  char supper[500];
} menu_t;

typedef struct fixed_ {
  char last_name[12];
  char first_name[12];
  char gender;
  char age[3];
  char year[4];
  char month[3];
  menu_t menus[31];
} fixed_t;

int main(int argc, char **argv) {
  FILE *fp = fopen(argv[1], "r");
  fixed_t fixed[500];
  int i;

  for (i=0; i<500; i++) {
    fread(&fixed[i], sizeof(fixed_t), 1, fp);
  }

  return 0;
}

今回の問題とは関係ないですが、一応知ってるとは思いますが、知らない人のために。

#pragmaなのでコンパイラ依存ですが、4byte境界を変更することができます。

次のような構造体をsizeofすると、5になります。
また、pragma packを取り除くと、8になります。

#BMPのローダーを書くときにお世話になったなぁ。
#頭の'BM'って文字列のせいで、全部が2byteずつずれて・・・。
1
2
3
4
5
6
7
#pragma pack(push,1)
typedef struct s
{
    char ch1;
    int i1;
} s;
#pragma pack(pop)

最近はあまり意識しなくなってしまいましたが
固定長のデータを構造体で読み取る場合には
共用体を併用したほうが安全です。
そのままでは、ビックエンディアンのときにおかしくなってしまうと思うので。。。
無名構造体とか使用するともう少し楽にアクセスできた気がするけど
普段しようしないので忘れてしまいました^^;;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
union {
    char stream [35];
    struct tagDATA
    {
        char family[12];
        char name[12];
        char sex;
        char age[3];
        char year[4];
        char month[3];
    } data;
} head;

read_size = read(fd, head.stream, sizeof(head.stream));

構造体に属する変数が詰まっている保証はないので、例えば4バイトアラインを基本とする処理系だったら、monthとmenusの間に1バイト使われない領域が入ってしまい、入力データのサイズと構造体のsizeofが一致しないかと。 もちろんこのコンパイラにこのオプション付ければ動く、というのはあるかもしれませんが、それはそれでタグを付けていただきたいところです。


#6098書いた者ですが、これ嘘?charのみを使ってるとアライメントされない?


わざわざありがとうございます。理解が曖昧なまま投稿してはいけませんねorz


全部文字として扱い、領域解放、エラー処理を無視して書くとこんな感じになりました。


cat > makedata.pl<<EOF
for(1..500)
{
print "666666666666";
print "555555555555";
print "F";
print "333";
print "4444";
print "11";
print "22";

print "9" for(1..500);
print "8" for(1..500);
print "7" for(1..500);
}
EOF
perl makedata.pl>data

gcc main.c
./a.out
4444

 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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include "stdio.h"
#include "string.h"
int main()
{
    struct {
        char *family;
        char *name;
        char *sex;
        char *age;
        char *year;
        char *month;

        char *date;
        char *breakfast;
        char *lunch;
        char *dinnar;
    } strdata[500];

    int i;
    char buff[501];
    FILE *fp;

    fp = fopen("data", "r");

    for(i =0; i < 500; i++) {
    /* fgets: \n か 読み込んだ文字列の最後に、\0を付加します
              \0 付加分だけ余分に +1  */
        fgets(buff, 12 + 1, fp);
        strdata[i].family = strdup(buff);

        fgets(buff, 12 + 1, fp);
        strdata[i].name = strdup(buff);

        fgets(buff, 1 + 1, fp);
        strdata[i].sex = strdup(buff);

        fgets(buff, 3 + 1, fp);
        strdata[i].age = strdup(buff);

        fgets(buff, 4 + 1, fp);
        strdata[i].year = strdup(buff);

        fgets(buff, 2 + 1, fp);
        strdata[i].month = strdup(buff);

        fgets(buff, 2 + 1, fp);
        strdata[i].date = strdup(buff);

        fgets(buff, 500 + 1, fp);
        strdata[i].breakfast = strdup(buff);

        fgets(buff, 500 + 1, fp);
        strdata[i].lunch = strdup(buff);

        fgets(buff, 500 + 1, fp);
        strdata[i].dinnar = strdup(buff);
    }

/* 34 番目 のとし */
    printf("%s", strdata[33].year);

}

Index

Feed

Other

Link

Pathtraq

loading...