こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

バイナリファイルの読み込み(C言語)

raw(音楽ファイル)データを配列rawに読み込みたいのですが,バイナリファイルの読み込み方がわかりません.

サンプルで以下のようなソース(途中略)があるのですが,
・なぜrawの型としてshortを使っているのか
・データ数の半分(file_size = ftell(fp) / 2)しか読み込んでいない
・fgetc(fp) << 8
あたりの意味がわからないので教えて下さい.

--------------------------------------------------------
short *raw;

if((fp=fopen(argv[1], "rb")) == NULL){
fprintf(stderr, "can't open %s.\n", argv[1]);
exit(1);
}
fseek(fp, 0, SEEK_END);
file_size = ftell(fp) / 2;
fseek(fp, 0, SEEK_SET);

raw = (short *)malloc((size_t)(file_size * sizeof(short)));
if(raw == NULL){
fprintf(stderr, "malloc error\n");
exit(1);
}
for(i=0;i<file_size;i++)
raw[i] = (short)((fgetc(fp) << 8) | fgetc(fp));
-----------------------------------------------------

投稿日時 - 2006-01-24 00:43:59

QNo.1917112

暇なときに回答ください

質問者が選んだベストアンサー

動作自体はNo.1,No.2の方のとおりですが、このサンプルには不都合な点が2点あります。

>file_size = ftell(fp) / 2;
2というマジックナンバを使っている、正式にはsizeof(short)でないとまずい。
file_size = ftell(fp) / sizeof(short);

>raw[i] = (short)((fgetc(fp) << 8) | fgetc(fp));
fgetcを式の中で2回呼び出している、C言語規格では未定義の動作で、fgetcを実行する順序がどうなるか分からないので、下のように書かないと駄目。
raw[i] = fgetc(fp) << 8;
raw[i] |= fgetc(fp);
または
raw[i] = fgetc(fp);
raw[i] |= fgetc(fp) << 8;
どちらかになるかは、元データのエンディアンによります。

投稿日時 - 2006-01-24 14:56:26

お礼

回答ありがとうございます。

>どちらかになるかは、元データのエンディアンによります。
入力データがリトルエンディアンかビッグエンディアンかでどちらかに決まる、ということですね。

投稿日時 - 2006-01-25 02:52:32

ANo.3

このQ&Aは役に立ちましたか?

1人が「このQ&Aが役に立った」と投票しています

回答(5)

ANo.5

fgetc(fp) & 0xff;
勘違いでした、必要ありません。
fgetc(fp);
だけでした、昔のCでfgetc(fp)の戻り値が-128~127とEOFが返ってくるものがありましたので、その対策です。
-128~-1を0x80~0xffに変換するためです、現在のCは0~255が返るため不要です。

投稿日時 - 2006-01-27 23:07:37

お礼

たびたびありがとうございます。
なんとか動きました。

投稿日時 - 2006-02-01 04:52:22

ANo.4

No.3の訂正です。
------ ここから ------
動作自体はNo.1,No.2の方のとおりですが、このサンプルには不都合な点が1点あります。

>raw[i] = (short)((fgetc(fp) << 8) | fgetc(fp));
fgetcを式の中で2回呼び出している、C言語規格では未定義の動作で、fgetcを実行する順序がどうなるか分からないので、下のように書かないと駄目。
ビッグエンディアンのとき
raw[i] = fgetc(fp) << 8;
raw[i] |= fgetc(fp) & 0xff;
または
リトルエンディアンのとき
raw[i] = fgetc(fp) & 0xff;
raw[i] |= fgetc(fp) << 8;
------ ここまで ------

以下は間違えていました。
>>file_size = ftell(fp) / 2;
>2というマジックナンバを使っている、正式にはsizeof(short)でないとまずい。
>file_size = ftell(fp) / sizeof(short);
逆でしたsizeof(short)がまずくて2の方が正式でした。
元ファイル自体が2バイトずつ書かれているのをsizeof(short)で計算するのは間違いでした。

投稿日時 - 2006-01-25 14:04:50

お礼

回答ありがとうございます。
>raw[i] = fgetc(fp) & 0xff;
8ビット全てで1とのアンドをとる、ということですよね。
なぜこのような操作をしているのでしょうか?

投稿日時 - 2006-01-26 02:46:07

ANo.2

rawファイルのデータ構造の知識はありませんが、サンプルソースを読むかぎりでは、
16ビットのデータが、リトルエンディアン(上位アドレスに下位バイトのデータが配置されている)
で格納されている、という前提でバイナリファイルを読み込む処理を行っています。

> なぜrawの型としてshortを使っているのか

確実に16ビットのデータとして扱いたいのだと思います。intでは処理系によってサイズが変わってきます。

> データ数の半分(file_size = ftell(fp) / 2)しか読み込んでいない

ftell、fseek、fgetc、などは全て、ファイルを1バイト単位で扱います。
ftellの返す値は、ファイルサイズをバイトで数えた数値です。
これに対し、raw = (short *)malloc((size_t)(file_size * sizeof(short)));
では、2バイト単位で領域を確保しているので、ftellの返す値の半分で十分です。
また、最後のforループでは、1回のループ内でfgetcを2回呼び出して、2バイトづつ読み込んでいますので
ファイル全体を読み込めています。

> fgetc(fp) << 8

バイトオーダーが変わらないように、fgetcで1バイトづつ読み込み、先に取得した8ビットのデータを
8ビット分上位にシフトして、空いた下位8ビットに、次に取得した8ビットをビット演算で格納し、
16ビットのデータにしてから、raw[i] に代入するための処理です。

投稿日時 - 2006-01-24 02:49:46

お礼

回答ありがとうございます。
ソースの内容はだいだいわかりましたが、
エンディアンあたりの話がよく分からないのでもう少し勉強してみます。

投稿日時 - 2006-01-25 02:48:40

ANo.1

short
 ->16bits
  ->2bytes
->2はfile_sizeのdivider
   ->file_size⇒short分取り出す回数
という連想ゲームかな?

投稿日時 - 2006-01-24 01:40:29

お礼

回答ありがとうございます。

一つのデータが2バイトだからshortを使っているということですね。

投稿日時 - 2006-01-25 02:20:06

あなたにオススメの質問