バイナリファイル
専用のアプリケーションソフトを使用することを前提とし、文字コードなどを考慮しないで作成されたファイルとして「バイナリファイル」があります。
テキストエディタなどで表示していると文字化けしてるの?
という感想をもってしまうような表示が見られると思います。
このサンプルではCSVファイル(カンマ区切りで区分したデータの集まりであるファイル)をバイナリファイルにする方法を見てみます。
今回使用するファイルは次の通りです。
ごく簡単な内容にしています。
商品名とおぼしきものと、価格と思しきものの2種類です。
商品名は文字列、価格は数字という構成です。
このデータを使用してCSVファイルからバイナリファイルを作成します。
それではサンプルを見てください。
CSVファイルをバイナリファイルに変換するサンプルです。
ここではCSVファイルを読み込み、バイナリファイルとして書きこんでいます。
そして、バイナリファイルを作り終えたら、作成したバイナリファイルを開いて内容を読み取っています。
それではサンプルの内容を見てみます。
11、12行目では「#define」を使用しています。
英語をそのまま読んだ意味合いで定義するということですが、プログラムをコンパイルするときに「#define」で定義した値や処理、マクロに置き換えてくれます。
このサンプルでは「MAX」が「2048」という値として使用していたり、
「GOODS_NAME」を「21」という値として使用しています。
このほかにもマクロといって、簡単なプログラム単位を登録することにより、実行可能なちょっとした関数のような働きをさせることも可能です。
よく使用されるマクロのサンプルとして円周率があります。
次のような使い方となります。
マクロのサンプル | #include | #define PI 3.14159 | #define CIRCLE(r) ( PI * r * r ) | | void main() | { | double r = 0.0f; | r = 10.0f; | printf("r = %f s = %f\n", r, CIRCLE( r ) ); | | #undef PI | | } |
|
上記サンプルでいう次の箇所がマクロとなります。
「#define CIRCLE(r) ( PI * r * r )」
ここで定義した「CIRCLE」をつかうことにより、
円周率のπ×半径×半径の「半径」を変数の値として設定するだけで、マクロに登録された計算結果を返してくれます。
下の方に書いてある「#undef PI」は「#define」で定義した内容を無効にするという意味合いです。
再定義しなおしたい場合に用います。
このサンプルでは登録した円周率の値として定義した「PI」を無効にするという意味合いとなります。
それではサンプルプログラムに戻ります。
45行目を見てください。
バイナリファイルを作成するため、バイナリファイル用のファイルを準備しています。
fopen_sの引数が「wb」となっているところがポイントです。
55行目からはCSVファイルを開き、1行ずつデータを読み取っています。
CSVファイルから読み取ったデータを61~66行目で商品名と価格の各変数に格納しています。
格納する方法として
文字列の場合は
strtok_s関数→strcpy_s関数
数字の場合は
strtok_s関数→atoi関数
を使って変換した値を各変数に格納しています。
strtok_s関数はセキュリティ上の観点からマイクロソフトさんが作成してくれた関数です。
使用方法はstrtok関数とほぼ同じです。
strtok_s関数は1回目と2回目の判定を「NULL」でないかどうか?
で判定しています。
※「strtok」関数のプログラムをみるとそうなっています(if文で第1引数のデータがNULLでないかどうかを判定する処理)。strtok_s関数もほぼ構成は同じようなはずです。
このサンプルで用いているCSVファイルのデータは、商品名の次に価格が並んでいます。
すなわち、最初は商品名です。
そこで、strtok_s関数の第1引数にはCSVファイルのデータをそのまま設定しています。
分割する文字列はカンマ区切りの「,」ですね。csv変数には「,\n」として区切り文字を設定しています。
第3引数はstrtok_s関数が内部で使用するための変数です。char型に「&」をつけて渡してあげてください。
この第3引数のありなしがstrtok関数と使い方が違う点となります。
このstrtok関数はトークンの最初のポインタを返してくれます。
もしもトークンが見つからなかった場合はNULLを返します。
このサンプルではポインタが返ってくるのでary変数に設定しています。
そして、このary変数を使ってstrcpy_s関数でstrData変数に値をコピーしています。
strcpy_s関数はマイクロソフトさんがセキュリティ上の安全性のため作成してくれた関数です。
使い方は次の通りです。
引数 | 内容 | 第1引数 | 格納先の変数を設定します。char 変数名[ n ];で宣言した変数をご利用ください。 ※「n」数字です。 | 第2引数 | _countof( 変数 )を使ってください。 | 第3引数 | コピーしたいデータまたは変数 |
価格の部分では値をコピーするために「atoi」関数を使用しています。
「atoi」関数は整数に変換してくれる関数です。
69、70行目を見てください。
上記で取得したCSVから取得したデータを各変数にしたものをバイナリファイルに書き込んでいます。
バイナリファイルに書き込むために「fwrite」関数を使用しています。
第1引数のデータをFILE出力ストリームである「fp2」へsizeof関数で取得した要素を1個分、出力しています。
数字の場合は「&(アドレス演算子)」を使っています。
バイナリファイルの書き込みが終わったので77、78行目のようにCSVファイル、バイナリファイルともに閉じています。
次に作成したバイナリファイルを開いてデータを確認しています。
83~105行目が該当部分です。
83行目のように最初にバイナリファイルを開いています。
今度は開いているので引数が「rb」となっています。
読み込んでいる行を知りたいため、91行目のように行数の初期化をしてます。
これで1行目から読み込んでいますという初期化をしていることになります。
バイナリを読み込んでいる部分が「fread」関数です。
ここではif文を使って、バイナリファイルを読み終わったかどうかの判定をしています。この処理では「1」でなかったらbreakすることにより処理を終えていますが、fread関数の戻り値の判定は次の通りです。
NO | 戻り値の内容 | 1 | 読み込みに成功した要素の個数を返します。 | 2 | 読み込みエラーか、ファイルの終わりになったときは要素の個数よりも小さくなることがあります。 | 3 | 要素1個の大きさ、もしくは、要素の個数が0の場合は0を返します。配列の内奥を変わらず、ストリームの状態も変化しません。 |
ちなみにfread関数の引数は次の通りです。
引数 | 引数の内容 | 第1引数 | 読み込む配列の格納先 | 第2引数 | 要素1個の大きさ | 第3引数 | 要素の個数 | 第4引数 | 入力ストリーム |
101行目は次の行数のためのカウントアップをしています。
105行目ではバイナリファイルを読み終えたので閉じています。
サンプルプログラムを実行した結果です。
入力ファイルに使用したCSVファイルとバイナリファイルをテキストエディタで開いた状態をキャプチャした画像です。
|
|