7.ファイルの取り扱い

Copyright(C) 11Feb2003 coskx

このページではCプログラムでファイルを取り扱う。
ここで修得してほしい内容は以下の通りである。
1.printfを用いたコンソール画面への出力と同じように,fprintfを使用してファイルへの出力が出来る。
2.scanfを用いたキーボードからの入力と同じように,fscanfを使用してファイルからの入力が出来る。
3.fgetc,fputc,fgets,fputsを使ってテキストファイルの処理が出来る
4.コマンドラインの取り込みと,引数を取り出すことが出来る
5.fread,fwriteを用いてバイナリファイルの入出力が出来る


演習における注意 ここでのプログラムの実行においては,実行形式ファイルおよび書き出すファイル,読み込み用ファイルはすべて同一ディレクトリに存在するものとし,実行はコンソール画面から行なう。

 7.1 ファイルへの書き出し

コンソール画面に文字を表示する時にはprintfを使った。
ファイルに文字を出力するにはfprintfを使う。

テキストを画面に表示する場合とファイルに書き出す場合のプログラムを並べて書いて比較してみよう。
「(A)画面出力」ではコンソール画面に文字が書 き出される。

「(B)ファイル出力」ではコンソール画面にはなにも出力されないがファイルmyfile.txtが出来る。テキストエディタでファイル myfile.txtを開いてみると「(A)画面出力」でコンソール画面に表示された内容と同じテキストが書いてあることが確認できる。

表示したいテキスト
Hello. How are you?
Fine, thanks. And You?
So so.

List7.1.1 文字出力プログラムの比較

(A)画面出力

(B)ファイル出力(ファイル名はmyfile.txt)

#include <stdio.h>

int main()
{
    printf("Hello. How are you?\n");
    printf("Fine, thanks. And You?\n");
    printf("So so.\n");
    return 0;
}

#include <stdio.h>
#include <stdlib.h> /*関数exitのために必要*/

int main()
{
    FILE *fp;
    fp=fopen("myfile.txt","w");
    if (fp==NULL) {
        printf("can't open a file\n");
        exit(1);
    }
    fprintf(fp,"Hello. How are you?\n");
    fprintf(fp,"Fine, thanks. And You?\n");
    fprintf(fp,"So so.\n");
    fclose(fp);
    return 0;
}


List7.1.1(B)のプログラムでファイルへ書き出すためのプログラムを検討しよう。
全部で4つの部分に分かれている。

項目

プログラムでの記述

1.プログラム中でファイルを表す変数を宣言する。
  変数宣言部で
    FILE *fp;
  宣言の時だけ「*」の付く「変な変数名!」ということ
  にしておこう。
  FILEは型名
  fpは変数名なので別の名前でもよい。
  この後,ファイルはファイル名ではなく,
  fp(ストリームと呼ばれる)で扱われる
FILE *fp;

2.ファイルのオープンとオープン失敗時の対策
  作ろうとするファイル名はここで決める。
  (この例ではmyfile.txt)
  "w"は「write」したいの意味である。

  何らかの理由でファイルが作れない場合は緊急停止
  (例えば,書き込み禁止領域にファイルを作ろうとした
  場合など)
  ファイルオープンに失敗するとfpはNULLになってしまう
  そこでfp==NULLの時は,失敗であることを表示して緊急停止

fp=fopen("myfile.txt","w");




if (fp==NULL) { /*ファイルのオープンに失敗した場合は*/
    printf("can't open a file\n"); /*失敗したと画面に表示*/
    exit(1); /*緊急停止*/
}
3.書き出したいことはここでfprintfで書く。
  いくらでも書ける。
fprintf(fp,"Hello. How are you?\n");
fprintf(fp,"Fine, thanks. And You?\n");
fprintf(fp,"So so.\n");
4.書き終えたらファイルをクローズする。
  クローズしてしまったらもう書きこめない。
fclose(fp);

ファイル名を実行時に決めることも出来る。次の例は出力ファイル名を実行時に決め,1000までの素数を書き出すものである。
List7.1.2 素数表(1000まで)のファイル出力

#include <stdio.h>
#include <stdlib.h> /*関数exitのために必要*/

int main()
{
    FILE *fp;
    char filename[256];
    int k;/*素数候補*/
    int max=1000;/*検査対象の最大値*/
    int n;/*約数候補*/
    int count=0;/*見つかった素数の個数*/

    printf("%dまでの素数を求めファイルに書き出します\n",max);
    printf("出力ファイル名を入力してください →");
    gets(filename);
   
fp=fopen(filename,"w");
    if (fp==NULL) { /*ファイルオープンに失敗したので*/
        printf("can't open a file\n"); /*失敗したと画面表示*/
        exit(1); /*緊急停止*/
    }

    fprintf(fp,"              %d までの素数\n",max);
    for (k=2;k<=max;k++) {
        n=2;/*約数候補の初期値*/
        while (k%n!=0) n++;
        if (n==k) { /*最初に見つかった約数が素数候補に等しい時*/
            fprintf(fp,"%4d ",k);
            count++;
            if (count%10==0) {
                fprintf(fp,"\n");
            }
        }
    }
    fprintf(fp,"\n");
    fprintf(fp,"素数は%d個見つかりました。\n",count);
   
fclose(fp);
    printf("素数は%d個見つかりました。\n",count);
    return 0;
}

/*****************コンソール出力********************
1000までの素数を求めファイルに書き出します
出力ファイル名を入力してください →primenumber.txt
素数は168個見つかりました。
****************************************************/

書き出された「primenumber.txt」
              1000 までの素数
   2    3    5    7   11   13   17   19   23   29
  31   37   41   43   47   53   59   61   67   71
  73   79   83   89   97  101  103  107  109  113
 127  131  137  139  149  151  157  163  167  173
 179  181  191  193  197  199  211  223  227  229
 233  239  241  251  257  263  269  271  277  281
 283  293  307  311  313  317  331  337  347  349
 353  359  367  373  379  383  389  397  401  409
 419  421  431  433  439  443  449  457  461  463
 467  479  487  491  499  503  509  521  523  541
 547  557  563  569  571  577  587  593  599  601
 607  613  617  619  631  641  643  647  653  659
 661  673  677  683  691  701  709  719  727  733
 739  743  751  757  761  769  773  787  797  809
 811  821  823  827  829  839  853  857  859  863
 877  881  883  887  907  911  919  929  937  941
 947  953  967  971  977  983  991  997
素数は168個見つかりました。

 課題7 その1


(1)x,x2,x3の表を1≦x≦100でファイル出力するプログラムを作りなさい。  (p07ex01.c)
出力されたファイルの内容を実行結果として提出ファイルに貼り付けなさい。

  x  x^2     x^3
  1    1       1
  2    4       8
  3    9      27
  4   16      64
  5   25     125
  :    :       :


 7.2 ファイルからの読み出し


プログラムのファイルととテキストファイルmydata1.txtを次の(B)のように作り,実行してみよう。

List7.2.1 データの読み出し

(A)キーボードからのデータ読み出しプログラム

#include <stdio.h>

int main()
{
    int data1,data2;
    scanf("%d %d",&data1,&data2);
    printf("data1=%d data2=%d\n",data1,data2);
    return 0;
}

(B)ファイルからのデータ読み出しプログラム

#include <stdio.h>
#include <stdlib.h> /*関数exitのために必要*/

int main()
{
    FILE *fp;
    int data1,data2;
    fp=fopen("mydata1.txt","r");
    if (fp==NULL) {
        printf("can't open a file\n");
        exit(1);
    }
    fscanf(fp,"%d %d",&data1,&data2);
    printf("data1=%d data2=%d\n",data1,data2);
    fclose(fp);
    return 0;
}

  テキストファイルmydata1.txt
(実行前に作っておく)

123 456

実行時のコンソール出力 実行時のコンソール出力
123 456
data1=123 data2=456
data1=123 data2=456

ファイルから読み出すためのプログラムを検討しよう。全部で4つの部分に分かれている。

項目

プログラムでの記述

1.プログラム中でファイルを表す変数を宣言する。
  変数宣言部で
    FILE *fp;
  宣言の時だけ「*」の付く「変な変数名!」ということ
  にしておこう。
  FILEは型名
  fpは変数名なので別の名前でもよい。
  この後,ファイルはファイル名ではなく,
  fp(ストリームと呼ばれる)で扱われる
FILE *fp;

2.ファイルのオープンとオープン失敗時の対策
  作ろうとするファイル名はここで決める。
  (この例ではmyfile.txt)
  "r"は「read」したいの意味である。

  何らかの理由でファイルが読めない場合は緊急停止
  (例えば,そのような名前のファイルが存在しない場合)
  ファイルオープンに失敗するとfpはNULLになってしまう
  そこでfp==NULLの時は,失敗であることを表示して緊急停止

fp=fopen("mydata1.txt","r");




if (fp==NULL) { /*ファイルのオープンに失敗した場合は*/
    printf("can't open a file\n"); /*失敗したと画面に表示*/
    exit(1); /*緊急停止*/
}

3.読み込みをfscanfで行なう fscanf(fp,"%d %d",&data1,&data2);
4.読み込み作業が終わったらファイルをクローズする。
  クローズしてしまったらもう読めない。
fclose(fp);


次 の例題として,複数の学生(100人未満)の英語・数学・国語のテストの点(0100)の処理を考えよう。学生番号を0からとし,英語・数学・国語の科目 番号を0,1,2とする。科目ごとの平均と各学生の3科目合計点を求める事にする。学生番号4の英語・数学・国語の得点をpoint[4] [0],point[4][1],point[4][2]とし,合計点はpoint[4][3]にしまうことにする。得点ファイルから読み込むこととす る。人数はプログラミング時点では不明で,得点ファイルにデータが存在するだけの人数がいることにする。
処理内容は,個人の3科目の合計点,各科目の平均点および個人合計点の平均点の算出とする。
List7.2.2 ファイル中の成績データの処理

#include <stdio.h>
#include <stdlib.h> /*関数exitのために必要*/

int main()
{
    FILE *fp;
    int point[100][4];
    int sum[4];
    double average[4]; /*英・数・国・(合計)の順の平均値*/
    int i,j,number;
    fp=fopen("mydata2.txt","r");
    if (fp==NULL) {
        printf("can't open a file\n");
        exit(1);
    }
    i=0;
    while (fscanf(fp,"%d,%d,%d",&point[i][0],&point[i][1],&point[i][2])==3) { /*下の説明参照*/
        i++;
    }
    fclose(fp);
    number=i;
    for (i=0;i<number;i++) {
        point[i][3]=0;
        for (j=0;j<3;j++) {
            point[i][3]+=point[i][j];
        }
    }
    for (j=0;j<4;j++) {
        sum[j]=0;
        for (i=0;i<number;i++) {
            sum[j]+=point[i][j];
        }
        average[j]=(double)sum[j]/(double)
number;
    }
    printf("学生番号 英語 数学 国語 合計\n");
    for (i=0;i<number;i++) {
        printf("%6d    %3d   %3d   %3d   %3d\n",
            i,point[i][0],point[i][1],point[i][2],point[i][3]);
    }
    printf("   平均    %.1f  %.1f  %.1f %.1f\n",
        average[0],average[1],average[2],average[3]);
    return 0;
}

fscanfに関する追加説明

関数fscanf()は,関数の返す値があり,実際に読み込むことに成功した数値の個数が返される。
そのため,
while ( fscanf(fp,"%d,%d,%d",&point[i][0],&point[i][1],&point[i][2])==3 ) {
は「fscanfで読み取れた値が3個ならばループ内処理を行う」,すなわち
「値を3個読み込むことに成功しているならばループ内処理を行う」の意味になる。
したがって,3が返されなくなった場合は,ファイルの終端に達したことがわかり,
このwhileループから抜け出し,読み込みを終える。

テキストファイルmydata2.txt (実行前に作っておく)
 86, 75, 60
 75, 68, 81
 84, 98,100
 87, 76, 48
 82, 58, 73
100, 87, 98
 64, 72, 70
 87, 68, 99
 98, 87, 68
 87, 72, 84
実行時のコンソール出力
学生番号 英語 数学 国語 合計
     0     86    75    60   221
     1     75    68    81   224
     2     84    98   100   282
     3     87    76    48   211
     4     82    58    73   213
     5    100    87    98   285
     6     64    72    70   206
     7     87    68    99   254
     8     98    87    68   253
     9     87    72    84   243
   平均    85.0  76.1  78.1 239.2

 

 課題7 その2

(2)次の枠内は,あるクラスの英語,数学,理科,国語の試験の得点一覧である。ファイルmarks.txtの中身である。これをダウンロードしなさい。(ブラウザで右クリック→ファイルに保存)
次に,このファイルを読み込み,List7.2.2のように各科目の平均点,学生ごとの総点,総点の平均(小 数点以下1桁まで)を求め,新たに一覧表ファイルmarks1.txtを作成するプログラムを作成しなさい。(p07ex02.c)
出力されたファイルの内容を実行結果として提出ファイルに貼り付けなさい。

  70,  59,  56,  66
 100, 100, 100, 100
  68,  90,  96,  94
  79,  92,  85,  75
  64,  66,  72,  50
 100, 100, 100, 100
  81,  92,  86,  97
  95,  88,  88,  79
  78,  74,  75,  74
  75,  80,  92,  75
  87,  76,  77,  63
  90,  90,  99, 100
  68,  68,  67,  75
  66,  66,  69,  71
  71,  69,  73,  80
  98,  81,  98,  97
 100, 100, 100, 100
  91, 100,  94,  92
  89,  87,  73,  68
  89, 100, 100,  94
  21,  27,  26,  25
 100, 100, 100, 100
  36,  53,  32,  31
  97,  74,  72,  85
  86,  85,  81,  79
  75,  71,  63,  72
  88,  94,  87,  84
  62,  68,  72,  60
  62,  68,  62,  70
  92,  88,  86,  70
  57,  64,  69,  64
  55,  77,  51,  73
  58,  74,  80,  71
  97,  91,  79, 100
  69,  58,  72,  69
  38,  54,  35,  19
  31,  23,  34,  37
  94,  85,  71,  63
  64,  90,  71,  73
  55,  51,  50,  48
  97, 100, 100, 100
  87,  83, 100,  85



 7.3 テキストファイルの処理

ファイルはバイトデータの集まりと考えることができる。
ファイルの内容を,バイトごとに16進表示するとファイルの原始的な姿が見える。
ファイルの内容をバイトごとに16進表示することをdump(ダンプ)という。
ダンプのための道具「kskdump.exe」があるので,ダウンロードして,学習に生かしてほしい。
 kskdump.exeのダウンロード先


 7.3.1 テキストファイルの構成

テキストエディタ (例えばterapadなど)で編集することの出来るファイルはテキストファイルと呼ばれる。Cのプログラムソースファイルもテキストファイルの1つであ る。もともとテキストファイルは,文字と改行コードが一列に並んでいるファイルである。これをテキストエディタなどのアプリケーションが改行コードのとこ ろで改行して人間に見せているので,ふだん見慣れたテキストとして見えるようになっている。例えばテキストエディタで


のように3行に見えるファイルは

ABCD[改行]EDG[改行]HIJKLMN[改行]

のように一列の文字列で構成されている。
( EOF は End Of File のことである。)
ファイルダンププログラムでファイルの中を見ると,次のように見える。

文字コードの説明は「A.変数 (A.4 文字コード)(APPENDIX 2 文字コードに関する補足)」を参照のこと

kskdump.exeで見たファイルの内容表示([改行]は0d 0aで保存されているのがわかる)
 kskdump.exeのダウンロード先

 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f 0123456789abcdef
0000000 :
0000010 :
 41 42 43 44 0d 0a 45 44 47 0d 0a 48 49 4a 4b 4c
 4d 4e 0d 0a
ABCD..EDG..HIJKL
MN..

同様に



のように3行に見えるファイルでは

kskdump.exeで見たファイルの内容表示([改行]は0d 0aで保存されているのがわかる)
 kskdump.exeのダウンロード先
ABCD[改行]EDG[改行]HIJKLMN

のように最後の改行がない場合もある。
ファイルダンププログラムでファイルの中を見ると,次のように末尾に0d 0aがないことがわかる。

kskdump.exeで見たファイルの内容表示([改行]は0d 0aで保存されているのがわかる)
 kskdump.exeのダウンロード先

 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f 0123456789abcdef
0000000 :
0000010 :
 41 42 43 44 0d 0a 45 44 47 0d 0a 48 49 4a 4b 4c
 4d 4e
ABCD..EDG..HIJKL
MN

また



このようなプログラムソースファイルもテキストファイルなので,

#include <stdio.h>[改行][改行]int main()[改行]{[改行]    printf("Hello. How are you?\n");[改行]
    printf("Fine, thanks. And You?\n");[改行]    printf("So so.\n");[改行]
    return 0;[改行]}[改行]

のように一列の文字列で構成されている。

kskdump.exeで見たファイルの内容表示([改行]は0d 0aで保存されているのがわかる)
 kskdump.exeのダウンロード先

 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f 0123456789abcdef
0000000 :
0000010 :
0000020 :
0000030 :
0000040 :
0000050 :
0000060 :
0000070 :
0000080 :
0000090 :
 23 69 6e 63 6c 75 64 65 20 3c 73 74 64 69 6f 2e
 68 3e 0d 0a 0d 0a 69 6e 74 20 6d 61 69 6e 28 29
 0d 0a 7b 0d 0a 20 20 20 20 70 72 69 6e 74 66 28
 22 48 65 6c 6c 6f 2e 20 48 6f 77 20 61 72 65 20
 79 6f 75 3f 5c 6e 22 29 3b 0d 0a 20 20 20 20 70
 72 69 6e 74 66 28 22 46 69 6e 65 2c 20 74 68 61
 6e 6b 73 2e 20 41 6e 64 20 59 6f 75 3f 5c 6e 22
 29 3b 0d 0a 20 20 20 20 70 72 69 6e 74 66 28 22
 53 6f 20 73 6f 2e 5c 6e 22 29 3b 0d 0a 20 20 20
 20 72 65 74 75 72 6e 20 30 3b 0d 0a 7d 0d 0a
#include <stdio.
h>....int main()
..{..    printf(
"Hello. How are
you?\n");..    p
rintf("Fine, tha
nks. And You?\n"
);..    printf("
So so.\n");..
 return 0;..}..


た だし,テキストファイルの形式はWindowsOSとUNIXOSでは[改行]のコードの取り扱いが異なっているが,ユーザプログラムがテキスト形式で ファイルを読み込むとOSがOS差を取り除いてくれるため,Cプログラミングでは[改行]のところはすべて'\n'(1文字)に見える。
OS
改行コード
Windows
0d + 0a
UNIX
0a のみ

プログラム中でファイルから1文字読み込むには関数fgetc()を用い,ファイルへ1文字書き出すには関数fputc()を用いる。
またファイルから改行コードまでの文字列を読み出すには関数fgets()を用い,ファイルに文字列を書き出すには関数fputs()を用いる。
詳しくは「
ライブラリ関数 C.6テキストファイル入出力関数」参照。

 7.3.2 テキストファイルの文字単位処理


テキストファイルを一文字ごとに処理する。
ここで大事な関数はfgetc()(ファイルからの1文字読み込み)とfputc()(ファイルへの1文字書き込み)である。
これらの関数については,「C.ライブラリ関数の説明」にまとめられている。

例1

テキストファイルから1文字読み込みで,ファイルの先頭の10文字を読み込み,画面に書き出すプログラム

読み込み対象ファイル「Peter.txt」
Peter ran straight away to Mr. McGregor's garden,
and squeezed under the gate!
First he ate some French beans;
and then he ate some radishes.

List 7.3.1 テキストファイルから1文字読み込みで,先頭10文字を読み込み,画面に書き出すプログラム 

#include <stdio.h>
#include <stdlib.h>

int main()
{
    FILE *fp;
    char fname[]="Peter.txt";
    int ch; /*読み込まれた文字(文字コード)*/
    int i;
   
    /*ファイルオープンの手続き*/
    fp=fopen(fname,"r");
    if (fp==NULL) {
        printf("can't open %s\n",fname);
        exit(1);
    }
   
    for (i=0; i<10; i++) { /*ファイル先頭の10文字読み込む*/
        ch=fgetc(fp);
        printf("%d文字目 [%c(%3d %02xH)]\n",i,ch,ch,ch);
    }
   
    /*ファイルクローズ*/
    fclose(fp);
    return 0;
}

実行結果(画面表示)「Peter ran 」までが読み込まれ表示されている。
0文字目 [P( 80 50H)]
1文字目 [e(101 65H)]
2文字目 [t(116 74H)]
3文字目 [e(101 65H)]
4文字目 [r(114 72H)]
5文字目 [ ( 32 20H)]
6文字目 [r(114 72H)]
7文字目 [a( 97 61H)]
8文字目 [n(110 6eH)]
9文字目 [ ( 32 20H)]

List 7.3.1のプログラムを修正して,ファイル全体を表示してみよう → 全ファイル表示


例2

テキストファイルのコピープログラム
テキストファイルをオープンし,1文字入力→1文字出力を繰返し,ファイルの終端まで繰り返す。

List 7.3.2 テキストファイルのコピー(1文字入力1文字出力)

実行時にコピー元ファイル名とコピー先ファイル名を聞かれるので,
「コピー元ファイル名」を聞かれた時に実在するテキストファイル名
「コピー先ファイル名」を聞かれた時に実在しないファイル名を答えること。

あらかじめチェック用テキストファイル「a.txt」をテキストエディタでつくってから実行し,
「b.txt」が出来たことを確認して,テキストエディタで「b.txt」を見るとよい。

関数fgetc()は,ファイル先頭から順にから1文字だけ読み取る関数である。ファイルの終わりに達してしまって,もう読み込むことが出来ない場合はEOFを返す。
関数fputc()はファイルに1文字だけ書き込む関数である。
  これらの関数については,
C.ライブラリ関数の説明」にまとめられている。

#include <stdio.h>
#include <stdlib.h> /*exit()のため必要*/

int main()
{
    FILE *fin;
    FILE *fout;
    char InputFileName[256];/*コピー元ファイル名*/
    char OutputFileName[256];/*コピー先ファイル名*/
    int ch;
    printf("コピー元のファイル名 : ");
    gets(InputFileName);/*コピー元ファイル名の取得*/
    printf("コピー先のファイル名 : ");
    gets(OutputFileName);/*コピー先ファイル名の取得*/
    fin=fopen(InputFileName,"r");
    if (fin==NULL) {
        printf("can't open file <%s>\n",InputFileName);
        exit(1);/*ファイルオープンエラーで緊急停止*/
    }
    fout=fopen(OutputFileName,"w");
    if (fout==NULL) {
        printf("can't open file <%s>\n",OutputFileName);
        exit(1);/*ファイルオープンエラーで緊急停止*/
    }
    ch=fgetc(fin); /*ファイルfinから1文字読み込み,chにしまう*/
    while (ch!=EOF) { /*chがファイル読み込みエラーでない限りの意味
,EOFとはファイルの終わりを示す定数*/
        fputc(ch,fout); /*文字chをファイルfoutに保存*/
        ch=fgetc(fin);  /*ファイルfinから1文字読み込み,chにしまう*/
    }
    fclose(fin);  /*ファイルfinを閉じる*/
    fclose(fout); /*ファイルfoutを閉じる*/
    return 0;
}

/*********実行時のコンソール画面**************
コピー元のファイル名 :
a.txt
コピー先のファイル名 : b.txt
********************************************/

a.txt
Fish is easy to prepare and quick to cook and is a healthy options for a wide range of tastes and ages, including semi-vegetarians. It is low in calories and fat and provides protein, vitamins and minerals.

b.txt
Fish is easy to prepare and quick to cook and is a healthy options for a wide range of tastes and ages, including semi-vegetarians. It is low in calories and fat and provides protein, vitamins and minerals.

式が値を持つことを利用するとwhileの部分をスマートに記述できる。    

例3

テキストファイルを読み込んで,文字コードを適当に変換して別のテキストファイルに書き出す
1文字読み込んで,書き出すまでに,文字をある規則に従って別の文字に変換すると,ファイルの書き換えを行なう事になる。

List 7.3.3 テキストファイルの小文字大文字変換(1文字入力1文字出力)

関数toupper()を用いて,小文字を大文字に変換する

あらかじめチェック用テキストファイル「a.txt」をテキストエディタでつくってから実行し,
「b.txt」が出来たことを確認して,テキストエディタで「b.txt」を見るとよい。

関数fgetc()は,ファイルの終わりに達してしまって,もう読み込むことが出来ない場合はEOFを返す。

#include <stdio.h>
#include <stdlib.h> /*exit()のため必要*/
#include <ctype.h> /*toupper()のため必要*/

int main()
{
    FILE *fin;
    FILE *fout;
    char InputFileName[256];/*コピー元ファイル名*/
    char OutputFileName[256];/*コピー先ファイル名*/
    int ch;
    printf("コピー元のファイル名 : ");
    gets(InputFileName);/*コピー元ファイル名の取得*/
    printf("コピー先のファイル名 : ");
    gets(OutputFileName);/*コピー先ファイル名の取得*/
    fin=fopen(InputFileName,"r");
    if (fin==NULL) {
        printf("can't open file <%s>\n",InputFileName);
        exit(1);/*ファイルオープンエラーで緊急停止*/
    }
    fout=fopen(OutputFileName,"w");
    if (fout==NULL) {
        printf("can't open file <%s>\n",OutputFileName);
        exit(1);/*ファイルオープンエラーで緊急停止*/
    }
    ch=fgetc(fin); /*ファイルfinから1文字読み込み,chにしまう*/
    while (ch!=EOF) { /*chがファイル読み込みエラーでない限り*/
        ch=toupper(ch);
        fputc(ch,fout); /*文字chをファイルfoutに保存*/
        ch=fgetc(fin);  /*ファイルfinから1文字読み込み,chにしまう*/
    }
    fclose(fin);
    fclose(fout);
    return 0;
}

/*********実行時のコンソール画面**************
コピー元のファイル名 :
a.txt
コピー先のファイル名 : b.txt
********************************************/
a.txt
Fish is easy to prepare and quick to cook and is a healthy options for a wide range of tastes and ages, including semi-vegetarians.
It is low in calories and fat and provides protein, vitamins and minerals.

b.txt
FISH IS EASY TO PREPARE AND QUICK TO COOK AND IS A HEALTHY OPTIONS FOR A WIDE RANGE OF TASTES AND AGES, INCLUDING SEMI-VEGETARIANS.
IT IS LOW IN CALORIES AND FAT AND PROVIDES PROTEIN, VITAMINS AND MINERALS.

toupperはASCIIコードのみに対応しているため,日本語の混じったテキストファイルに対してこの操作を行なうと,その結果は保証されません。

もし,ファイル内のアルファベットをすべて小文字にしたかったら
どのようなプログラムになるだろうか考えなさい    

例4

ファイルを1文字ずつ読み込み,ある規則に従って,コード変換して,別なファイルに出力することで,暗号化できる。
例えば,ASCIIコードでは,0x20から0x7eまではアルファベット,数字,記号である。
ある文字コードがこの範囲の値だったら,3文字前にずらすことを考えよう。例えば 'D'→'A','E'→'B','F'→'C',のようにする。
これは,ある文字のコードから3を引くことによって実現できる。ただし,引いた結果が0x20未満になったら,0x5fを加えて,変換結果が0x20から0x7eの範囲を保つことを保証することとする。また暗号化したものを元に戻すにはこの逆の操作を行なえばよい。

List 7.3.4 Cソースファイルの暗号化

#include <stdio.h>
#include <stdlib.h> /*exit()のため必要*/

/*暗号化関数*/
int encodeChar(int ch)
{
    if (0x20<=ch && ch<=0x7e) {
        ch-=3;
        if (ch<0x20) ch+=0x5f;
    }
    return ch;
}

/*復号化関数*/
int decodeChar(int ch)
{
    if (0x20<=ch && ch<=0x7e) {
        ch+=3;
        if (0x7e<ch) ch-=0x5f;
    }
    return ch;
}

int main()
{
    FILE *fin;
    FILE *fout;
    char InputFileName[256];/*読み込みファイル名*/
    char OutputFileName[256];/*書き出しファイル名*/
    int ch;
    int direction;
    printf("読み込みファイル名 : ");
    gets(InputFileName);/*読み込みファイル名の取得*/
    printf("書き出しファイル名 : ");
    gets(OutputFileName);/*書き出しファイル名の取得*/
    do {
        printf("(1)暗号にする (2)復号する    ( 1 or 2 ) :");
        scanf("%d",&direction);
    } while (direction<1||2<direction);
    fin=fopen(InputFileName,"r");
    if (fin==NULL) {
        printf("can't open file <%s>\n",InputFileName);
        exit(1);/*ファイルオープンエラーで緊急停止*/
    }
    fout=fopen(OutputFileName,"w");
    if (fout==NULL) {
        printf("can't open file <%s>\n",OutputFileName);
        exit(1);/*ファイルオープンエラーで緊急停止*/
    }
    ch=fgetc(fin);
    while (ch!=EOF) { /*chがファイル読み込みエラーでない限り*/
        if (direction==1) ch=encodeChar(ch);
        else ch=decodeChar(ch);
        fputc(ch,fout);
        ch=fgetc(fin);
    }
    fclose(fin);
    fclose(fout);
    return 0;
}

=====================暗号化の際のコンソール画面の様子=====================

読み込みファイル名 : a.c
書き出しファイル名 : b.txt
(1)暗号にする (2)復号する    ( 1 or 2 ) :1

a.c

#include <stdio.h>

int main()
{
    printf("Hello. How are you?\n");
    printf("Fine, thanks. And You?\n");
    printf("So so.\n");
    return 0;
}


b.txt

 fk`irab|9pqafl+e;
fkq|j^fk%&
x
||||mofkqc%~Ebiil+|Elt|^ob|vlr<Yk~&8
||||mofkqc%~Cfkb)|qe^khp+|>ka|Vlr<Yk~&8
||||mofkqc%~Pl|pl+Yk~&8
||||obqrok|-8
z

=====================復号化の際のコンソール画面の様子=====================

読み込みファイル名 : b.txt
書き出しファイル名 :
c.txt
(1)暗号にする (2)復号する    ( 1 or 2 ) :2

c.txt

#include <stdio.h>

int main()
{
    printf("Hello. How are you?\n");
    printf("Fine, thanks. And You?\n");
    printf("So so.\n");
    return 0;
}


 7.3.3 テキストファイルの行単位処理


テキストファイルから一行を文字列として読み込んで処理する。
ここで大事な関数はfgets()(ファイルからの1行読み込み)とfputs()(ファイルへの1行書き込み)である。
これらの関数については,「C.ライブラリ関数の説明」にまとめられている。

例5


例1で使ったテキストファイル「Peter.txt」を1行ずつ読み込んで最初の3行を表示してみよう。

読み込み対象ファイル「Peter.txt」
Peter ran straight away to Mr. McGregor's garden,
and squeezed under the gate!
First he ate some French beans;
and then he ate some radishes.

List 7.3.5 テキストファイルから1行ずつ読み込んで最初の3行を表示する

#include <stdio.h>
#include <stdlib.h>

int main()
{
    FILE *fp;
    char fname[]="Peter.txt";
    char string[512]; /*読み込まれた1行文字列(文字コード)*/
    int i;
   
    /*ファイルオープンの手続き*/
    fp=fopen(fname,"r");
    if (fp==NULL) {
        printf("can't open %s\n",fname);
        exit(1);
    }
   
    for (i=0; i<3; i++) { /*ファイル先頭の3行の文字列を読み込む*/
        fgets(string,510,fp);
        printf("%d行目文字列 [%s]\n",i,string);
    }
   
    /*ファイルクローズ*/
    fclose(fp);
    return 0;
}

実行結果(画面表示)
0行目文字列 [Peter ran straight away to Mr. McGregor's garden,
]
1行目文字列 [and squeezed under the gate!
]
2行目文字列 [First he ate some French beans;
]

実行結果が3行にならず,「 ] 」が次の行に表示されているのは,関数fgetsが,改行コードも文字列の一部として取り込んでしまい,
文字列(char型配列)
stringの行末に改行コードが入っているからである。(関数fgetsの説明を参照のこと)

テキストファイルのイメージ
Peter ran straight away to Mr. McGregor's garden,[改行]and squeezed under the gate![改行]
First he ate some French beans;[改行]and then he ate some radishes.

1行目を読み込んだイメージ
Peter ran straight away to Mr. McGregor's garden,[改行]

2行目を読み込んだイメージ
and squeezed under the gate![改行]

3行目を読み込んだイメージ
First he ate some French beans;[改行]

もし改行コードが入っていなかったら,実行結果は次のようになるはずである。

仮想実行結果(現実にはこのようにはならない)
行末に改行コードが入っていなかったらこうなるはず
0行目文字列 [Peter ran straight away to Mr. McGregor's garden,]
1行目文字列 [and squeezed under the gate!]
2行目文字列 [First he ate some French beans;]

1行読み込んだ時,末尾についている改行コードが邪魔な時がある。
その時は,工夫して,改行コードなしで1行を読み込む関数fgets_modを作ろう。 → 関数fgets_mod()
あるいは,与えた文字列の行末の改行コードを取り去る関数removeLFを作ろう。  → 関数removeLF()

例6


例5で使ったプログラムを変更し,各行の文字コードを表示し,改行コード(16進で0A)が入っているのを確認しよう。
ファイル中では改行コードは0d 0aの2バイトだったが,windowsでは,ファイルから読み出した後には0aのみ1バイトである。
表示はされないが,0aの後ろには文字列の終わりを示す00が入っている。

テキストファイルは「Peter.txt」を使用する。

List 7.3.6 テキストファイルから1行ずつ読み込んで最初の3行の文字コード表示を行なう

#include <stdio.h>
#include <stdlib.h>

void printString(char txt[])
{
    int length;
    int i;
    length=strlen(txt); /*txtに含まれる文字数を得る*/
    for (i=0; i<length; i++) {
        printf("%02x ",txt[i]);
        if (i%20==19) printf("\n");
    }
    if (i%20!=0) printf("\n");
}

int main()
{
    FILE *fp;
    char fname[]="Peter.txt";
    char string[512]; /*読み込まれた1行文字列(文字コード)*/
    int i;
   
    /*ファイルオープンの手続き*/
    fp=fopen(fname,"r");
    if (fp==NULL) {
        printf("can't open %s\n",fname);
        exit(1);
    }
   
    for (i=0; i<3; i++) { /*ファイル先頭の3行の文字列を読み込む*/
        fgets(string,510,fp);
        printf("%d行目文字列 [%s]\n",i,string);
        printString(string);
        printf("\n");
    }
   
    /*ファイルクローズ*/
    fclose(fp);
    return 0;
}

実行結果(画面表示)

0行目文字列 [Peter ran straight away to Mr. McGregor's garden,
]
50 65 74 65 72 20 72 61 6e 20 73 74 72 61 69 67 68 74 20 61
77 61 79 20 74 6f 20 4d 72 2e 20 4d 63 47 72 65 67 6f 72 27
73 20 67 61 72 64 65 6e 2c 0a

1行目文字列 [and squeezed under the gate!
]
61 6e 64 20 73 71 75 65 65 7a 65 64 20 75 6e 64 65 72 20 74
68 65 20 67 61 74 65 21 0a

2行目文字列 [First he ate some French beans;
]
46 69 72 73 74 20 68 65 20 61 74 65 20 73 6f 6d 65 20 46 72
65 6e 63 68 20 62 65 61 6e 73 3b 0a

行末に改行コード(16進で0A)が入っているのがわかる。その他は文字コード表で確認しておきなさい。


例7

プログラムソースファイルを行ごとに読み込んで,行頭に行番号をつけて,テキストファイルに書き出す。

List 7.3.7 プログラムソースファイルを読み込んで,行頭に行番号をつけて,テキストファイルに書き出す。

あらかじめチェック用プログラムソースファイル「a.c」をテキストエディタでつくってから実行し,
「b.txt」が出来たことを確認して,テキストエディタで「b.txt」を見るとよい。

関数fgets()は,ファイルから一行を読み込み文字列にする。
一行とは,改行文字があるところまでの文字列。

関数fgets()では入力文字列に行端の'\n'が含まれるため,
この文字列をprintfなどで書き出す場合には,書式中に'\n'を付け足す必要はない。

関数fgets()は,ファイルの終わりに達してしまって,もう読み込むことが出来ない場合はNULLを返す。
関数fputs()はファイルに文字列を書き出す。
  これらの関数については,「C.ライブラリ関数の説明」にまとめられている。

#include <stdio.h>
#include <stdlib.h> /*exit()のため必要*/

int main()
{
    FILE *fin;
    FILE *fout;
    char InputFileName[256];/*読み込みファイル名*/
    char OutputFileName[256];/*書き出しファイル名*/
    char textbuffer[512]; /*テキストを読み込む作業用文字列*/
    int lineNumber=1;

    /*ファイル名のキーボード入力*/
    printf("読み込みファイル名 : ");
    gets(InputFileName);/*読み込みファイル名の取得*/
    printf("書き出しファイル名 : ");
    gets(OutputFileName);/*書き出しファイル名の取得*/
    fin=fopen(InputFileName,"r");

    /*ファイルのオープン*/
    if (fin==NULL) {
        printf("can't open file <%s>\n",InputFileName);
        exit(1);/*ファイルオープンエラーで緊急停止*/
    }
    fout=fopen(OutputFileName,"w");
    if (fout==NULL) {
        printf("can't open file <%s>\n",OutputFileName);
        exit(1);/*ファイルオープンエラーで緊急停止*/
    }

    /*読み込み-書き出し作業*/
    while (fgets(textbuffer, 512, fin) != NULL) { /*ファイル読み込みエラーでない限り*/
        /*fgets(textbuffer, 512, fin)とはファイルfinから一行読み込み,文字配列textbufferに*/
        /*保存しなさいの意味。ただし,文字列長制限があり511文字まで*/
        fprintf(fout,"%04d %s", lineNumber, textbuffer);
                    /*textbufferの末尾に改行コードが含まれているため,書式中に\nは不要*/
        lineNumber++;
    }

    /*ファイルのクローズ*/
    fclose(fin);
    fclose(fout);

    return 0;
}

/*********実行時のコンソール画面**************
読み込みファイル名 :
a.c
書き出しファイル名 : b.txt
********************************************/

a.c
このプログラム

b.txt
0001 #include <stdio.h>
0002 #include <stdlib.h> /*exit()のため必要*/
0003
0004 int main()
0005 {
0006     FILE *fin;
0007     FILE *fout;
0008     char InputFileName[256];/*読み込みファイル名*/
0009     char OutputFileName[256];/*書き出しファイル名*/
0010     char textbuffer[512]; /*テキストを読み込む作業用文字列*/
0011     int lineNumber=1;
0012
0013     /*ファイル名のキーボード入力*/
0014     printf("読み込みファイル名 : ");
0015     gets(InputFileName);/*読み込みファイル名の取得*/
0016     printf("書き出しファイル名 : ");
0017     gets(OutputFileName);/*書き出しファイル名の取得*/
0018     fin=fopen(InputFileName,"r");
0019
0020     /*ファイルのオープン*/
0021     if (fin==NULL) {
0022         printf("can't open file <%s>\n",InputFileName);
0023         exit(1);/*ファイルオープンエラーで緊急停止*/
0024     }
0025     fout=fopen(OutputFileName,"w");
0026     if (fout==NULL) {
0027         printf("can't open file <%s>\n",OutputFileName);
0028         exit(1);/*ファイルオープンエラーで緊急停止*/
0029     }
0030
0031     /*読み込み-書き出し作業*/
0032     while (fgets(textbuffer, 512, fin) != NULL) { /*ファイル読み込みエラーでない限り*/
0033         /*fgets(textbuffer, 512, fin)とはファイルfinから一行読み込み,文字配列textbufferに*/
0034         /*保存しなさいの意味。ただし,文字列長制限があり511文字まで*/
0035         fprintf(fout,"%04d %s", lineNumber, textbuffer);
0036                     /*textbufferの末尾に改行コードが含まれているため,書式中に\nは不要*/
0037         lineNumber++;
0038     }
0039
0040     /*ファイルのクローズ*/
0041     fclose(fin);
0042     fclose(fout);
0043
0044     return 0;
0045 }

 

 課題7 その3

(3)C ソースプログラムファイルを読み込み,何行で構成されていたか,何文字(改行などの制御コードも1文字として数える)で構成されていたかをカウントするプログ ラムを作成しなさい。なお,検査対象ファイル名は,コマンドラインからか,または,getsで読み込むものとする。出力はコンソール画面とする。(ファイ ルの最終文字が改行コードである場合,ない場合に対応しているかどうか確認すること)(p07ex03.c)
厄介なヒント 行数を数える場合,「'\n'」を数えればよいと思いますか?
ファイルによっては最終行の終わりに,「'\n'」が付いていないものもある。

提出実行結果は,出力されたコンソール画面とします。
次のファイルをダウンロード・解凍してテストしなさい。正解はファイル中に書いてある。
 
テスト用ファイル

ただし,実行結果に関しては,テスト用ファイル名がexample.txt,行数が100,文字数が2560のときは
The file [example.txt] has 100 lines and 2560 characters.
と表示するようにしなさい。



(4)Cソースプログラムファイルを読み込み,行ごとに,逆順ならびにして,別のファイル名で保存しなさい。なお,ファイル名は,コマンドラインからか,または,getsで読み込むものとする。逆順ならびとは次のようなものである。(p07ex04.c)

提出実行結果は,出力されたファイルの内容を貼り付けることとします。
p05ex09.cで作った関数が使えるはずである。(正しく動作していれば)

#include "stdio.h"
#include "stdlib.h"
#include "ctype.h"
   :

"h.oidts" edulcni#
"h.bildts" edulcni#
"h.epytc" edulcni#
   :
のようになる。プログラミングにおいて関数void reverseString(char string[])を作り,用いなさい。
void reverseString(char string[])は与えられた文字列stringを逆順にする関数である。
なお,ファイルから一行文字列を読み込むときにはfgets()を用いると思うが,これは行末に'\n'をつけたままで
読み込むので,これを取り除いてからreverseString()を使って反転し,ファイル書き出しの際に,'\n'を
付け加えるのが良い。
なお,実行の検査には次のファイルを用いなさい。(ダウンロード可能)

test.c.zip

#include <stdio.h>

int main()
{
    printf("Hello. How are you?\n");
    printf("Fine, thanks. And You?\n");
    printf("So so.\n");
    return 0;
}

(5)入力ファイル中の単語を抜き出し出力ファイルへ書き出すプログラムを作成しなさい。単語とはアル ファベットで始まり,アルファベットと数字とアンダーバー(_)のみでできている文字列である。単語の書き出しにおいては,単語ごとにスペースで区切っ て,8つごとに改行するものとする.例えば何かのプログラムを入力ファイルとした時,次のようなファイルができるでしょう。なお,ファイル名は,コマンド ラインからか,または,getsで読み込むものとする。(p07ex05.c)
 参考プログラム(単語を抜き出して画面に表示している。課題とは異なる)

include stdio h include stdlib h include ctype
h void main int argc char argv FILE
f1 f2 short int ch if argc printf

次のファイル(ダウンロード可能)について検証しなさい。(12qwe は数字で始まるため単語ではないので抽出されてはならない)

test.txt.zip

1234 ..wer qwe123+wer456
sw_qwe,q12_12,12qwe,A123B123
wsed_123

発展問題(挑戦課題です。力をつけたい人は挑戦してください)

(6)「(5)」で入力ファイル中の単語を抜き出し,出力ファイルへ書き出したが,下のようにそれぞれの単語の出現回数を数えてその結果を出力ファ イルに書き出しなさい。(ここまでに習得した文法のみを使うなら,2次元配列とカウンタ変数の配列が必要です。また「5.5 文字列操作の関数」や「C.ライブラリ関数の説明」で紹介したstrで始まる関数を便利に使います。文字列コピー関数(strcpy)や文字列の一致を調 べる関数(strcmp)があります。 チャレンジ課題) (p07ex06.c)

include   3
stdio   1
h   3
stdlib   1
ctype   1
main   1

参考プログラム ポインタ,構造体を使用している


 7.4 コマンドラインの取り込み

UNIXのコンソール,Windowsのコマンドプロンプト,Windowsのファイルのドロップに対応した内容
これはC言語の文法とは関わりはないが,将来システムプログラミングなどでは必須になる内容である。
興味があったらここを読むこと

興味があったらここを読むこと   


 7.5 バイナリファイルの処理

テキストファイルは文字コードと改行などの制御コードで出来ている。そのためエディタと呼ばれる種類のアプリケーションで内容を見たり,変更することが容易である。
これに対して,画像やサウンドを保存するファイルは,データのコンピュータ内部表現をそのまま保存しているファイルである。
例えば音を表現する時は,1/8000秒ごとの8ビットの数値の連続で音の波形データを表わすことがある。
例えば,グラフで音の波形を表すと次のようになるが,

これを数値であらわすと,
0,31,56,68,66,50,25,-2,-24,-36,-33,-16,11,41,68,86,89,77,53,23,-6,-27,-36,-31,-12,13,40,....
の ようになる。データをこのままテキストファイルとして保存してもよいが,1ポイントあたり4文字程度(4バイト)必要となる。しかし,各データは8ビット であることが約束されているため,コンピュータ内部表現では,1バイトである。これをそのまま保存すると1ポイントあたり1バイトとなり,テキストファイ ルで保存するのに比べ1/4のサイズのファイルとなる。
(実際のサウンドファイルや画像ファイルではファイルサイズを小さくするための工夫がなされ,ファイルの先頭にはヘッダーブロックと呼ばれる必要な一連のデータが保存されている部分がある。)

バイナリファイルはテキストエディタでは見ることができず,バイナリエディタやバイナリビュワーで見ることができる。
kskdump.exeを使うと内容を見ることができる。
kskdump.exeのダウンロード先

テキストファイルとバイナリファイルの書き出しと読み込みを比較してみよう。

テキストファイルの書き出しと読み込み バイナリファイルの書き出しと読み込み

/*テキストファイル"sample.txt"の書き出し*/
#include <stdio.h>
#include <stdlib.h> /*関数exitのために必要*/

int main()
{
    FILE *fp;
    short int data[]={
        1,2,3,4,5,6,7,8,9,10,
        11,12,13,14,15,16,17,18,19,20
    };
    int dataSize;
    int i;
    fp=fopen("sample.txt","w");
    if (fp==NULL) {
        printf("can't open a file\n");
        exit(1);
    }
    dataSize=sizeof(data)/sizeof(short int);
    /*要素数を求めた (dataのバイト数)÷(short intのバイト数)*/
    for (i=0;i<dataSize;i++) {
        fprintf(fp,"%d\n",data[i]);
    }
    fclose(fp);
    return 0;
}

/*バイナリファイル"sample.bin"の書き出し*/
#include <stdio.h>
#include <stdlib.h> /*関数exitのために必要*/

int main()
{
    FILE *fp;
    short int data[]={
        1,2,3,4,5,6,7,8,9,10,
        11,12,13,14,15,16,17,18,19,20
    };
    int dataSize;
    int n;
    fp=fopen("sample.bin","wb");
    if (fp==NULL) {
        printf("can't open a file\n");
        exit(1);
    }
    dataSize=sizeof(data)/sizeof(short int);
    /*要素数を求めた (dataのバイト数)
÷(short intのバイト数)*/
    n=fwrite(data, sizeof(short int), dataSize, fp);
    printf("%d個書き込みました\n",n);
    fclose(fp);
    return 0;
}

/*実行画面**
20個書き込みました
***********/

説明
sizeof()は与えられた項目のバイト数を得る演算子
sizeof(short int)は2となる
(short intは2バイトであるため)
配列dataの要素数は20個であるため,メモリ上では40バイトになっ
ているはずである。
そのため,sizeof(data)は40バイトとなる。
要素数は,40バイトを1要素当たりのバイト数2で割ったものになる
はずだから
dataSize=sizeof(data)/sizeof(short int)は要素数になる。
説明
dataSize=sizeof(data)/sizeof(short int)に関しては左の説明
の通り,要素数である。
fwrite(data, sizeof(short int), dataSize, fp);
は,配列dataを書き込むが,sizeof(short int)バイト
(これはshort intは2バイトなので2を表す)ずつ
dataSize個(20個),ストリームfpに書き込みなさい
という関数,関数の返す値は書き込めた項目数なので
20が返された。
テキストエディタでsample.txtを表示すると次のようになる。
1
2
3
4
 : 途中省略
18
19
20

テキストエディタでsample.binを表示しようとしても,
表示できない。

/*テキストファイル"sample.txt"の読み込み*/
#include <stdio.h>
#include <stdlib.h> /*関数exitのために必要*/

int main()
{
    FILE *fp;
    short int data[100];
    int dataSize;
    int i;
    fp=fopen("sample.txt","r");
    if (fp==NULL) {
        printf("can't open a file\n");
        exit(1);
    }
    i=0;
    while (fscanf(fp,"%d",&data[i])==1) {
        i++;
    }
    dataSize=i;
    printf("%d個読み込みました\n",dataSize);
    for (i=0;i<dataSize;i++) {
        printf("(%02d) %2d\n",i+1,data[i]);
    }
    fclose(fp);
    return 0;
}

/****実行結果****
20個読み込みました
(01)  1
(02)  2
(03)  3
 : :
(18) 18
(19) 19
(20) 20
********************/

/*バイナリファイル"sample.bin"の読み込み*/
#include <stdio.h>
#include <stdlib.h> /*関数exitのために必要*/

int main()
{
    FILE *fp;
    short int data[100];
    int dataSize;
    int i;
    fp=fopen("sample.bin","rb");
    if (fp==NULL) {
        printf("can't open a file\n");
        exit(1);
    }
    dataSize=fread(data,sizeof(short int),100,fp);
    printf("%d個読み込みました\n",dataSize);
    for (i=0;i<dataSize;i++) {
        printf("(%02d) %2d\n",i+1,data[i]);
    }
    fclose(fp);
    return 0;
}

/****実行結果****
20個読み込みました
(01)  1
(02)  2
(03)  3
 : :
(18) 18
(19) 19
(20) 20
********************/

  説明
fread(data, sizeof(short int), 100, fp);
は,2バイト(short intは2バイト)ずつ
100個までのデータを,ストリームfpから,配列data中に読み出しなさい
という関数,関数の返す値は読み込んだ項目数なので
20が返され,dataSizeに20が格納され10この値が得られた。

20個のデータしか読み込まないのに,
short int data[100];
となっているのは,読み込み時には,データがあふれる実行時エラーが
起こる可能性があるため,配列の大きさを大きめに取ります。
また
fread(data,sizeof(short int),100,fp);
の100というのは読み出すデータの数ではなく,読み込み可能な最大デ
ータ数です。
Windows PCでの
テキストファイル"sample.txt"の内容
(後半の表示は,データを文字として表示したものである。
表示できないデータは,「.」で表している。)
kskdump.exeで表示したもの
Windows PCでの
バイナリファイル"sample.bin"の内容
(後半の表示は,データを文字として表示したものである。
表示できないデータは,「.」で表している。)
kskdump.exeで表示したもの

sample.txt
     +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f
00 : 31 0d 0a 32 0d 0a 33 0d 0a 34 0d 0a 35 0d 0a 36
10 : 0d 0a 37 0d 0a 38 0d 0a 39 0d 0a 31 30 0d 0a 31
20 : 31 0d 0a 31 32 0d 0a 31 33 0d 0a 31 34 0d 0a 31
30 : 35 0d 0a 31 36 0d 0a 31 37 0d 0a 31 38 0d 0a 31
40 : 39 0d 0a 32 30 0d 0a
     0123456789abcdef
00 : 1..2..3..4..5..6
10 : ..7..8..9..10..1
20 : 1..12..13..14..1
30 : 5..16..17..18..1
40 : 9..20..
sample.bin
     +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f
00 : 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00
10 : 09 00 0a 00 0b 00 0c 00 0d 00 0e 00 0f 00 10 00
20 : 11 00 12 00 13 00 14 00                        
     0123456789abcdef
00 : ................
10 : ................
20 : ........


参考 1 WindowsOSとUNIXでのテキストファイルの改行の取扱の違い

テキストファイル"sample.txt"の書き出し出力されたsample.txt
#include <stdio.h>
#include <stdlib.h> /*関数exitのために必要*/

int main()
{
    FILE *fp;
    short int data[]={
        1,2,3,4,5,6,7,8,9,10,
        11,12,13,14,15,16,17,18,19,20
    };
    int dataSize;
    int i;
    fp=fopen("sample.txt","w");
    if (fp==NULL) {
        printf("can't open a file\n");
        exit(1);
    }
    dataSize=sizeof(data)/sizeof(short int);
    /*要素数を求めた (dataのバイト数)÷(short intのバイト数)*/
    for (i=0;i<dataSize;i++) {
        fprintf(fp,"%d\n",data[i]);
    }
    fclose(fp);
    return 0;
}

同じプログラムを実行しても,OSによって作成されるファイルは異なっている。
WindowsOSでは改行コード \n (LF)を出力した場合, 0d 0a(\r\n)(CR+LF)に変換され出力されるが,
UNIXではそのまま 0a (\n) (LF)のみが出力される。
CR: Carriage Return (文字ポインタを先頭に戻す。かつてのテレタイプで活字をのせたキャレッジを左端に戻していた。)
LF: Line Feed (改行する。かつてのテレタイプで紙送り機構が1行分紙を送った。)

Windows PCでの
テキストファイル"sample.txt"の内容
kskdump.exeで表示したもの
UNIXでの
テキストファイル"sample.txt"の内容
kskdump.exeで表示したもの
sample.txt
     +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f
00 : 31 0d 0a 32 0d 0a 33 0d 0a 34 0d 0a 35 0d 0a 36
10 : 0d 0a 37 0d 0a 38 0d 0a 39 0d 0a 31 30 0d 0a 31
20 : 31 0d 0a 31 32 0d 0a 31 33 0d 0a 31 34 0d 0a 31
30 : 35 0d 0a 31 36 0d 0a 31 37 0d 0a 31 38 0d 0a 31
40 : 39 0d 0a 32 30 0d 0a
     0123456789abcdef
00 : 1..2..3..4..5..6
10 : ..7..8..9..10..1
20 : 1..12..13..14..1
30 : 5..16..17..18..1
40 : 9..20..
sample.txt
     +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
00 : 31 0a 32 0a 33 0a 34 0a 35 0a 36 0a 37 0a 38 0a
01 : 39 0a 31 30 0a 31 31 0a 31 32 0a 31 33 0a 31 34
02 : 0a 31 35 0a 31 36 0a 31 37 0a 31 38 0a 31 39 0a
03 : 32 30 0a                                        
     0123456789ABCDEF
00 : 1.2.3.4.5.6.7.8.
01 : 9.10.11.12.13.14
02 : .15.16.17.18.19.
03 : 20.


WindowsOSのプログラムでUNIX形式のファイルを作成したい

WindowsOSのプログラムでUNIX形式のテキストファイルを作成したいことがある。
その場合は,ファイルの開き方を変えれば可能である。(変更は1箇所)

fp=fopen("sample.txt","w");
   ↓
fp=fopen("sample.txt","wb");

このようにするとfprintffputs,fputcなどで改行コード \n を出力した場合, 0d 0a(\r\n)に変換されず,
そのまま 0a (\n) のみが出力され,UNIX形式のテキストファイルになる。
バイナリ出力の1つの使い方である。


すでにあるWindowsOS形式のテキストファイルをUNIX形式に変換したい

多くのテキストエディタは,このような変換機能を持っている。
例えば,terapadでもファイルメニューに「文字/改行コード指定保存」というのがあって,これを実行すると,
改行コードを変更した保存が可能である。
変換して再読込しても,テキストエディタで見る限り,文字コードの違いはわからない。

参考 2 intel(インテル)系CPU,とMotrola(モトローラ)系CPUによる複数バイト変数のバイトの並び順

WindowsPCはIntel系のCPUなので2バイトの表現時,順番が逆になる
「01 00」の2バイトで0001(10進数でも1)を表す。
「02 00」の2バイトで0002(10進数でも2)を表す。
  :
「0f 00」の2バイトで000f(10進数では15)を表す。
「10 00」の2バイトで0010(10進数では16)を表す。
  :
このようなバイト順のことをリトルエンディアン(little-endian)と呼ぶ。


motrola系のCPUでは2バイトの表現時の順番は順である。
「00 01」の2バイトで0001(10進数でも1)を表す。
「00 02」の2バイトで0002(10進数でも2)を表す。
  :
「00 0f」の2バイトで000f(10進数では15)を表す。
「00 10」の2バイトで0010(10進数では16)を表す。
  :
このようなバイト順のことをビッグエンディアン(big-endian)と呼ぶ。

Windows PC(intel系CPU使用)での
バイナリファイル"sample.bin"の内容
モトローラ系CPUでの
バイナリファイル"sample.bin"の内容
sample.bin
     +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f
00 : 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00
10 : 09 00 0a 00 0b 00 0c 00 0d 00 0e 00 0f 00 10 00
20 : 11 00 12 00 13 00 14 00                        
     0123456789abcdef
00 : ................
10 : ................
20 : ........
sample.bin
     +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
00 : 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08
01 : 00 09 00 0a 00 0b 00 0c 00 0d 00 0e 00 0f 00 10
02 : 00 11 00 12 00 13 00 14
     0123456789ABCDEF
00 : ................
01 : ................
02 : ........



 課題7 その4


(7)double型配列をバイナリファイルに格納し,もう一度読み出して,もとのデータが復元されることを確認しなさい。
1つのプログラム内の前半でファイルに書き出し,後半で読み込むようにする。
(p07ex07.c)

テストするデータと確認用作業は次のとおりとする。必要な変数は自分で増やすこと。

double ddata[10]={
    1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0
};
double gdata[10];
   :
printf("original data\n");
for (i=0;i<????;i++) printf("(%02d)  %6.1f\n",i,ddata[i]);
   :
ここでddataをfwriteでファイルに書き込む
   :
ここでファイルからfreadでgdataに読み込む
   :
printf("obtained data\n");
for (i=0;i<????;i++) printf("[%02d] %6.1f\n",i,gdata[i]);


文章課題)ファイルに関するこのページの内容で重要なポイントをまとめてレポートにしなさい。ただし,初めの2行(ファイル名,ID,出席番号,氏名)は,これまでのプログラムと同様な書式で書くこと。 (p07.txt)

ただし,次のキーワードは必ず含むこと

ファイル,ファイルのオープン,ファイルのクローズ,
テキストファイル,バイナリファイル