メモのコツを教えてください!

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));
-----------------------------------------------------

A 回答 (5件)

動作自体は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;
どちらかになるかは、元データのエンディアンによります。
    • good
    • 0
この回答へのお礼

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

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

お礼日時:2006/01/25 02:52

fgetc(fp) & 0xff;


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

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

お礼日時:2006/02/01 04:52

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)で計算するのは間違いでした。
    • good
    • 0
この回答へのお礼

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

お礼日時:2006/01/26 02:46

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] に代入するための処理です。
    • good
    • 0
この回答へのお礼

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

お礼日時:2006/01/25 02:48

short


 ->16bits
  ->2bytes
->2はfile_sizeのdivider
   ->file_size⇒short分取り出す回数
という連想ゲームかな?
    • good
    • 0
この回答へのお礼

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

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

お礼日時:2006/01/25 02:20

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています


おすすめ情報