工学部 6 7 8 9 10 組 ( 奇数学籍番号 ) 担当 : 長谷川英之 情報処理演習 第 14 回 2011 年 1 月 20 日 1
今日のテーマ ファイル入出力 ですが, キーボード入力などもおさらいします 2
標準入力 キーボードで入力 : 標準入力という例 )scanf( %d,&i) 前回までの講義でファイルからデータを読み込む場合に使用した関数 : fscanf 例 )fscanf(fin, %d,&i) fscanf は, ファイルからの入力以外にも使用できる. ファイルからデータを読み込んだ場合の fin などはファイルポインタであったので, ファイルから入力される. したがって,fin の代わりに標準入力を示す stdin を使用すれば,fscanf を使用してキーボード入力ができる. 例 )fscanf(stdin, %d,&i) scanf では, 暗示的に標準入力 stdin が指定されている. つまり, scanf( %d,&i) と fscanf(stdin, %d,&i) は同一 3
標準出力 コンソールに表示 : 標準出力という例 )printf( %d,i) 前回までの講義でファイルにデータを書き込む場合に使用した関数 : fprintf 例 )fprintf(fin, %d,i) fprintf は, ファイルへの出力以外にも使用できる. ファイルにデータを書き込んだ場合の fin などはファイルポインタであったので, ファイルへ出力される. したがって,fin の代わりに標準出力を示す stdout を使用すれば,fprintf を使用してコンソールへ出力できる. 例 )fprintf(stdout, %d,i) printf では, 暗示的に標準出力 stdout が指定されている. つまり, printf( %d,i) と fprintf(stdout, %d,i) は同一 4
1 文字だけの標準入力, 標準出力 h14.c #include <stdio.h> main() { int c; c=getchar(); int getchar() 1 文字を標準入力する関数. 文字を入力するが, 関数自体はint 型として定義されている. int putchar(int c) 1 文字を標準出力する関数. 文字を出力するが, 引数はint 型である. } putchar(c); 1 文字を標準する場合でも, 例 ) a (Enter) と入力する必要がある. つまり,a と, 改行を示す n が保存される必要がある.a を表現するために 1 バイト必要なので char 型 (1 バイトでは a しか記憶できないので問題となる. そこで,int 型 (2 バイトもしくは 4 バイト ) として定義されている. 5
アスキーデータとバイナリデータ アスキー (ASCII) 形式ヒトに分かりや易いテキスト ( 文字 ) として表現するためのデータフォーマット. 例えば,6 や b などの半角文字は全て 1 バイト (8 bit, 2 進数で 8 桁 ) のアスキーコードで表現され, 標準出力関数はそのコードをもとに,6 や b などと表示する. バイナリ形式文字や数字などを全て 2 進数で表現したもの. 全てアスキー形式で表現した方が分かり易いのでは? 例 )short 型 (2バイト) の変数 a=16384をアスキー形式およびバイナリ形式で表現アスキー形式 00110001 00110110 00110011 00111000 00110011 1 6 3 8 3 8 bit 5 = 40 bit (5バイト) バイナリ形式 16383 (10 進数 )=0011111111111111 (2 進数 ) 16 bit (2 バイト ) アスキーデータの方がデータサイズとしては大きくなる. 6
ファイル入出力の手順 ファイルポインタの宣言 FILE *fp; ファイルを開く fp=fopen( ファイル名, モード ); ファイルを読む / 書き込む fscanf, fprintf など ファイルを閉じる fclose(fp) 7
ファイルのオープン ファイルを開く : fp=fopen( ファイル名, モード ); ファイル名 のファイルを, 指定した モードで開く 下記のモードが使用可 モード 動作 ファイルがある場合 ファイルがない場合 r 読み込み ファイル内容を読込可 エラー : NULLを返す w 書き込み 元の内容を破棄して上書き 新規作成 a 追加 最後に追加書き込み 新規作成 r+ 読込 & 書込 読み込み & 書き込み可 エラー : NULLを返す w+ 読込 & 書込 読み込み & 書き込み可 元の内容は破棄される 新規作成 a+ 読込 & 追加書込読込 & 最後に追加書込新規作成 8
#include <stdio.h> main() { int a; float x; char s[16]; FILE *fp; アスキーファイルの入出力 scanf( %d %f %s,&a,&x,s); fp=fopen( test.txt, w ); fprintf(fp, %d %f %s,a,x,s); fclose(fp); fp=fopen( test.txt, r ); fscanf(fp, %d %f %s,&a,&x,s); fclose(fp); } printf( a, x, s = %d, %f, %s n,a,x,s); 9
アスキーファイルの入出力 : 1 文字入出力 #include <stdio.h> main() { int a; FILE *fp; a=getchar(); fp=fopen( test.txt, w ); putc(a,fp); fclose(fp); fp=fopen( test.txt, r ); a=getc(fp); fclose(fp); } putchar(a); 10
#include <stdio.h> main() { char s[16]; FILE *fp; アスキーファイルの入出力 : 1 行入出力 scanf( %s,s); } fp=fopen( test.txt, w ); fputs(s,fp); fclose(fp); fp=fopen( test.txt, r ); fgets(s,15,fp); fclose(fp); printf( %s n,s); この例の場合,15-1=14 個の文字がファイルから読み込まれ, s[0] から s[13] に格納される. s[14] には, 文字列の最後を示すヌル文字 ( 0) が格納される. もし, ファイルに書かれていた文字の数が 14 個より少ないときは, 行の末尾まで ( 改行コードが検出されるまで ) 読み込む. 11
バイナリデータの読み込み : 心電図データのダウンロード まず, 心電図データを http://www.ecei.tohoku.ac.jp/~hasegawa/ からダウンロード ダウンロードしたファイルは皆さんが作る C プログラムと同じディレクトリに保存して下さい. この 2 つをダウンロードして下さい. 12
#include <stdio.h> #define nnn 850 main() { int i; float ecg[nnn+1]; FILE *fp; バイナリファイルの入出力 fp=fopen("./ecg.bin","rb"); fread(ecg,sizeof(float),nnn,fp); fclose(fp); fp=fopen("./ecg2.txt","w"); for(i=0;i<nnn;++i) fprintf(fp,"%f n",ecg[i]); fclose(fp); バイナリデータを読み込む 読み込んだバイナリデータを, アスキーデータとして保存. (gnuplot で確認するため ) } fp=fopen("./ecg2.bin","wb"); fwrite(ecg,sizeof(float),nnn,fp); fclose(fp); 読み込んだバイナリデータを, バイナリデータとして再度保存 ( 元のファイルと同じサイズになるか確認するため ). 13
バイナリデータ入出力方法 fopen のモード指定モードの指定は, アスキーデータの場合はこれまで r ( 読み込み ) もしくは w ( 書き込み ) のみ指定していましたが, バイナリデータの場合は, rb ( 読み込み ), wb ( 書き込み ) を使用します. バイナリデータ読み込み関数 fread 使用法 : fread( 読込データを保存する配列名,sizeof( 配列の型 ), データの数, ファイルポインタ ) 例 : fread(ecg,sizeof(float),nnn,fp) 意味 : ファイルポインタfpが示すファイルから,floatのサイズ(4バイト) のデータを nnn 個読み込んで, 配列 ecgに格納. 格納するときのデータ型は配列の型とする. sizeof() は, カッコ内に指定したデータ型のサイズを示す. バイナリデータ書き込み関数 fwrite 使用法 : fwite( 読込データを保存する配列名,sizeof( 配列の型 ), データの数, ファイルポインタ ) 例 : fread(ecg,sizeof(float),nnn,fp) 意味 : 配列 ecgのnnn 個の要素それぞれを, その配列のデータ型 (float) のサイズ (4バイト) でファイルポインタfpが示すファイルに書き込む. 14
アスキーデータとバイナリデータのサイズの違い [xxxxxxxxxxxxxxxxxxxxxxxxxx]% ファイルサイズ (bytes) ecg.bin は,ecg.txt( アスキーデータ ) をバイナリデータに変換しただけですが, アスキーデータ (ecg.txt) に比べ, バイナリデータ (ecg.bin) はかなり小さくなっていることが分かります. 15
バイナリデータの読み込み結果の確認 [xxx]% gnuplot gnuplot> plot ecg.txt w l gnuplot> replot ecg2.txt w l ecg.bin が正常に読み込まれていれば,ecg.txt と ecg2.txt は一致 16
fwrite による書き込み結果の確認 [xxxxxxxxxxxxxxxxxxxxxxxxxx]% ファイルサイズ (bytes) ecg.bin は,ecg.txt( アスキーデータ ) をバイナリデータに変換しただけですが, アスキーデータ (ecg.txt) に比べ, バイナリデータ (ecg.bin) はかなり小さくなっていることが分かります. ecg2.bin が正常に書き込まれていれば, 元のデータファイル ecg.bin のサイズと同一になります. 17
課題 Q14-20110120 http://www.ecei.tohoku.ac.jp/hasegawa/ の第 14 回のところにある課題用データ (data.bin) をダウンロードする. データファイルdata.binには, 本資料 13ページで扱ったecg.binと同様に数値データがバイナリ形式で保存されている (1 行に羅列 ). データの数は 75 75=5625, データの型 shortである.data.binのデータを, 本講義資料 13ページのプログラムと同様の要領でfreadを用いて読み出して配列 img[i] に格納する. 読み込んだデータは75 点 75 点の画像データなので, 次ページ以降の要領でgnuplotで3 次元表示できる形式のテキストファイル (data.txt) に保存するプログラムを作成して下さい (data.txtのデータの並べ方は, 第 4 回資料 8ページも参考になると思います ). まずは, 本資料 13ページのようにファイルからfreadを用いてバイナリデータを配列 img[i] に格納するところを参考にプログラムを作り始めて下さい. 後は, 配列 img[i] の内容をgnuplotで表示できるようにテキストファイルに保存すればOKです. 18
データが並んでいるイメージ img[0] img[1] img[2] img[3] data.bin に入っているデータの構造 i = 0, j = 0 のデータ i = 1, j = 0 のデータ i = 2, j = 0 のデータ i = 73, j = 0 のデータ i = 74, j = 0 のデータ i = 0, j = 1 のデータ i = 1, j = 1 のデータ i = 74, j = 1 のデータ i = 0, j = 2 のデータ i = 74, j = 74 のデータ 画像は点の集合 2 次元画像であれば, 座標 (i, j) の点の色を数値データで表現する. 全部で 75 75 個のデータ 0 1 2... 74 = j 0 1 2... 74 = i 画像 ( 点の集合 )
img[0] img[1] img[2] img[3] データが並んでいるイメージ gnuplot で表示させたときのデータの並び i = 0, j = 0 のデータ i = 1, j = 0 のデータ i = 2, j = 0 のデータ i = 73, j = 0 のデータ i = 74, j = 0 のデータ i = 0, j = 1 のデータ i = 1, j = 1 のデータ i = 74, j = 1 のデータ i = 0, j = 2 のデータ i = 74, j = 74 のデータ gnuplot の 3 次元表示で下記のように表示されるようにしたい 波形 j j = 1 j = 2 j = 3 j = 74 j = 75 i 番目 i=1 i=2 i=3 i=74 i=75 各マスには波形 i の j 番目の数値データが色で表示される. 20
変換前データファイル data.bin データをファイルに出力する際の並べ方 変換後データファイル ( ファイル名は何でも良い ) i= 0, j= 0 のデータ i= 1, j= 0 のデータ i= 2, j= 0 のデータ i=73, j= 0 のデータ i=74, j= 0 のデータ i= 0, j=1 のデータ i= 1, j=1 のデータ i=74, j=1 のデータ i= 0, j=2 のデータ i=74, j=74 のデータ i j データ 0 0 i= 0, j= 0 のデータ 1 0 i= 1, j= 0 のデータ 2 0 i= 2, j= 0 のデータ 73 0 i=73, j= 0 のデータ 74 0 i=74, j= 0 のデータ 0 1 i= 0, j=1 のデータ 1 1 i= 1, j=1 のデータ 74 1 i=74, j=1 のデータ 0 2 i= 0, j=2 のデータ 74 74 i=74, j=74 のデータ j( 行 ) の変わり目に空行が必要 j( 行 ) の変わり目に空行が必要 21
変換後データファイルを gnuplot で 3 次元表示 [xxx]$ gnuplot gnuplot> set pm3d map gnuplot> splot data.txt 変換後データファイル名が data.txt の場合 並べ替えた結果が正しいかどうかは表示結果を見ればすぐに分かると思います. 22
以降, 第 4 回資料より抜粋 ( 参考 ) 23
課題 (Q4-20101028c) 参考 本資料 7 ページで計算した関数 z = sin(x) sin(y) は,x, y, z の 3 次元データであるため, グラフ表示も 3 次元にする必要がある. 本資料 7 ページのプログラムで計算したデータを, 次ページに記す,gnuplot で 3 次元表示するためのフォーマットに並べ替えてアスキー ( テキスト ) ファイルに保存するプログラムを作成して下さい. 前ページのプログラムの計算結果を, 次のページで説明してある形式でファイルに出力する方法を考える, ということです. 24 24
[xxx]$ gnuplot gnuplot> set pm3d gnuplot> splot result.txt z gnuplot で 3 次元表示するためのファイルフォーマット 参考 gnuplot は3 次元表示も可能 3 次元表示に必要なファイルの構造今回は set pm3d map と入力. 点と点の間隔を細かくして滑らかに表示させるなど工夫してみて下さい. 様々な関数を表示できます. x y z x y z の値 -3.000000-3.000000 0.019915-2.400000-3.000000 0.095321-1.800000-3.000000 0.137429-1.200000-3.000000 0.131529-0.600000-3.000000 0.079682-0.000000-3.000000 0.000000 0.600000-3.000000-0.079682 1.200000-3.000000-0.131529 1.800000-3.000000-0.137429 2.400000-3.000000-0.095321 3.000000-3.000000-0.019915-3.000000-2.400000 0.095321-2.400000-2.400000 0.456250-1.800000-2.400000 0.657798-1.200000-2.400000 0.629558-0.600000-2.400000 0.381395-0.000000-2.400000 0.000000 0.600000-2.400000-0.381395 1.200000-2.400000-0.629558 1.800000-2.400000-0.657798 2.400000-2.400000-0.456251 3.000000-2.400000-0.095322-3.000000-1.800000 0.137429-2.400000-1.800000 0.657798-1.800000-1.800000 0.948379-1.200000-1.800000 0.907664-0.600000-1.800000 0.549876......... y = 0 の場合のデータ y の値が変わるところに空行が必要 y = 1 の場合のデータ 25
課題 Q4-20101028c の解答例 #include <stdio.h> #include <math.h> #define pi 3.1416 #define kizami 12 main() { int i,j; float phase,phase2,result; FILE *fout; } fout=fopen("result.txt","w"); for(i=0;i<kizami;++i){ for(j=0;j<kizami;++j){ phase=2*pi*i/kizami; phase2=2*pi*j/kizami; result=sin(phase)*sin(phase2); fprintf(fout,"%d %d %f n",i,j,result); } fprintf(fout," n"); } fclose(fout); 26