• ベストアンサー

バイナリファイルの読み込み(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)); -----------------------------------------------------

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

  • ベストアンサー
  • TT414
  • ベストアンサー率18% (72/384)
回答No.3

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

mintpil7
質問者

お礼

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

その他の回答 (4)

  • TT414
  • ベストアンサー率18% (72/384)
回答No.5

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

mintpil7
質問者

お礼

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

  • TT414
  • ベストアンサー率18% (72/384)
回答No.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)で計算するのは間違いでした。

mintpil7
質問者

お礼

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

  • harlan
  • ベストアンサー率77% (234/303)
回答No.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] に代入するための処理です。

mintpil7
質問者

お礼

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

  • onosuke
  • ベストアンサー率67% (310/456)
回答No.1

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

mintpil7
質問者

お礼

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

関連するQ&A

  • VisualC++でのバイナリファイル

    VisualC++でのバイナリファイルのサイズの取得。 Win7 x64 VC++2012 調べた所によると、 バイナリモードではNULL文字を使用できるため、 FILE* fp; fopen_s(&fp,"data.bin","rb"); fseek(fp,0,SEEK_END); size_t fsize=ftell(fp_cl); //fseek(fp,0L,SEEK_SET);//元の位置に戻す。 fclose( fp ); のような感じでサイズを取得してはいけない。 (fseekのSEEK_END動作が不定のため) ……と思っていたのですが、他に方法と言えば、 ファイルを一度全て空読みするくらいしかないので、 試にやってみたところ、上手く行ってしまいます。 一応NULLの入ったバイナリでも正しく取得できてしまいました。 これはCの仕様が変わったためなのか、VC++がたまたま対応しているのか、 どちらでしょうか? また、他に上手い方法があればよろしくお願いいたします。

  • 【C言語】ファイルを読み込んで16進数で表示する

    ファイルをバイナリモードで読み込んで16進数で表示するプログラムについて質問です。 以下の処理内容が理解できません。どのような処理を行っているのでしょうか?? (xdump.c)********************************* #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { int i, j; FILE *fp; if(NULL == (fp = fopen(argv[1], "rb"))) { fprintf(stderr, "Cannot open FILE[%s].\n", argv[1]); exit(1); } for(i=0; i<0x7FFF; ++i) { printf("%08X :", ftell(fp)); for(j=0; j<16; ++j) { int c; c = 0x00FF & getc(fp); if(ferror(fp)) { puts("\a>>>> Read Error ! <<<<"); break; } if(feof(fp)) break; /* 16進数で表示 */ printf(" %2X", c); } printf(" : %08X\n", ftell(fp)-1); /* : */ /* : */ /* 以下省略 */ ****************************************** 上記に関して、 最初のfor文から"c = 0x00FF & getc(fp);"までの処理内容が よくわかりません。どなたかご教授願えますでしょうか?

  • ファイルサイズの取得について

    2つのテキストファイルのサイズを取得し、そのファイルサイズ分だけを動的にメモリを確保しようとしています。 int *c,*a;と宣言し、 fp=fopen("./data/Problem.txt","r");//1つ目のファイル fseek(fp, 0, SEEK_END); /* ファイルの終端までシーク */ size = ftell(fp); /* 終端の位置、すなわちファイルサイズを得る */ fseek(fp, 0, SEEK_SET); /* ファイルの先頭に戻る */ c = (int *)malloc(size); /* ファイルサイズ分メモリ確保 */ while((x=fgetc(fp))!=EOF){ c[i]=x; i++; } c[i]='\0'; i=0; fclose(fp); fpa=fopen("./data/Answer.txt","r");//2つ目のファイル fseek(fpa, 0, SEEK_END); size = ftell(fpa); fseek(fpa, 0, SEEK_SET); a = (int *)malloc(size); while((x=fgetc(fpa))!=EOF){ a[n]=x; n++; } a[n]='\0';//・・・・(1) n=0; fclose(fpa); とすると1つ目のファイルの方だけはうまくいくのですが、(1)の部分で 「sample.exeの0x00411dcでハンドルされていない例外が発生しました:0xc0000005:場所0x0000000に書き込み中にアクセス違反が発生しました。。」 というエラーが出ます。 また、 int *c,*a;を int *c,a[300]; のように片方を配列として宣言し、 //a = (int *)malloc(size); /* ファイルサイズ分メモリ確保 */ のようにコメントアウトすると上記のエラーは出ずにcにメモリは確保されているようです。 これは何故なのでしょうか? また、どうすればaとcでメモリを確保出来るようになるのでしょうか? よろしくお願いいたします。

  • CSVファイルを読み込み構造体のメンバ、"value"に格納し、その後

    CSVファイルを読み込み構造体のメンバ、"value"に格納し、その後平均値を求めて構造体のメンバ、"ave"に格納し表示させたいのですが、読み込み格納している最中で、セグメンテーション違反で終了してしまいます。どなたかよろしければ教えて頂けないでしょうか。 プログラム ************************************************** #include <stdio.h> #include <string.h> #include <stdlib.h> #define SIZE 64 #define FILE_NAME_00 "f_00_01.CSV" struct Data{ double value; double ave; }; int main(int argc, char *argv[]) { FILE* fp,*fo; // ファイルポインタ用 int n, i, file_size; struct Data *dat; if ((fp = fopen(FILE_NAME_00,"r")) == NULL) { printf( "file open error\n" ); exit(EXIT_FAILURE); } fseek(fp, 0, SEEK_END); file_size = ftell(fp); dat = (struct Data*)malloc(file_size); printf("malloc address= %p, file size= %d\n", dat, file_size); fseek(fp, 0, SEEK_SET); i = 0; for(i=0;i<file_size;i++){ fscanf(fp,"%lf",&dat[i].value); printf("%lf\n",dat[i].value); i++; } fclose(fp); printf("\n"); free(dat); return 0; } *************************************************** f_00_01.CSV *************************************************** 2.313725 2.312810 2.314031 2.316167 2.315557 2.313725 . . . . . ***********************************************

  • C言語 バイナリファイルをfloatにしたいのですが…

    16進数で表示すると以下のようなバイナリファイルがあります。 42 b9 e0 a4 3b df ea c0 3a 70 eb dc 37 7c f4 8c これを4バイトずつfloatに入れて出したいのですが、 本当はこのように出したいのですが、 [0][92.93875] [1][0.0068334043] [2][9.190419E-4] [3][1.50773085E-5] このような値がでてしまいます [0][-9.745835E-17] [1][-7.339750E+00] [2][-5.301601E+17] [3][-3.766891E-31] ソースはこれです。 #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { FILE *fp; long size, n; float *buf; int i = 0; if (argc != 2) return 1; /* FILE OPEN */ fp = fopen(argv[1], "rb"); if (fp == NULL) return 1; /* FILE SIZE */ fseek(fp, 0, SEEK_END); size = ftell(fp); rewind(fp); printf("FILE SIZE %ld\n", size); /* FILE READ */ buf = malloc(size); if (buf == NULL) return 1; n = fread(buf, 1, size, fp); /* FILE PRINT */ for(i=0;i<10;i++) { printf("[%d][%E]\n", i, buf[i]); } return 0; } 本日一日調べましたが良くわかりませんでした。 開発環境は以下の内容です。 $ uname -a Linux fedoracore6 2.6.20-1.2962.fc6 #1 SMP Tue Jun 19 19:27:14 EDT 2007 i686 athlon i386 GNU/Linux $ gcc --version gcc (GCC) 4.1.2 20070626 (Red Hat 4.1.2-13) Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 何かお知恵を拝借出来ないものでしょうか?? よろしくお願い致しますm(_ _)m

  • csvファイルの読み込み

    fscanf関数を用いて、csvファイルの内容を構造体のそれぞれのメンバに読み込んで表示させようとしているのですが、4列目以降が上手く読み込めません。初歩的な質問で申し訳ありませんがどなたか教えてください。 ****************************ソース************************************************ #include <stdio.h> #include <string.h> #include <stdlib.h> #define SIZE 64 #define FILE_NAME "view_001_McdlData.csv" #define FILE_SIZE 819200 struct Data{ int DataNo; int FrameNo; int SampleNo; char Digital; double voltage; double trigger; double C; double D; }; int main(int argc, char *argv[]) { FILE* fp,*fo, *fi; // ファイルポインタ用 int n, i, file_size; double time,vel[FILE_SIZE]; struct Data *dat; char buff[SIZE]; if ((fp = fopen(FILE_NAME,"r")) == NULL) { printf( "file open error\n" ); exit(EXIT_FAILURE); } fseek(fp, 0, SEEK_END); file_size = ftell(fp); dat = (struct Data*)malloc(file_size); printf("malloc address= %p, file size= %d\n", dat, file_size); fseek(fp, 0, SEEK_SET); i = 0; //データの読み込み while((fscanf(fp, "%d,%d,%d,%s,%lf,%lf,%lf,%lf",&dat[i].DataNo,&dat[i].FrameNo,&dat[i].SampleNo,&dat[i].Digital,&dat[i].voltage,&dat[i].trigger,&dat[i].C,&dat[i].D)) !=EOF ){ printf("%d %d %d %s %lf %lf %lf %lf\n",dat[i].DataNo,dat[i].FrameNo,dat[i].SampleNo,dat[i].Digital,dat[i].voltage,dat[i].trigger,dat[i].C,dat[i].D); i++; } fclose(fp); return 0; } ***********************************csvファイルの内容************************************ 0,1,0,0x3F,2.270000,0.000000,-1.000000,-1.000000 1,1,1,0x3F,2.260000,0.010000,-1.000000,-1.000000 2,1,2,0x3F,2.260000,0.010000,-1.000000,-1.000000 3,1,3,0x3F,2.260000,0.010000,-1.000000,-1.000000 4,1,4,0x3F,2.260000,0.010000,-1.000000,-1.000000 5,1,5,0x3F,2.260000,0.000000,-1.000000,-1.000000 ***************************************************************************************************

  • ファイルの大きさを調べたいのですが、、、(C言語)

    2週間前にC言語の勉強を始めたばかりです。 「あるファイルのサイズを見て、メモリを確保し、  読み出した内容をソートして、同じファイルに再び書き出す」 というプログラムを作ろうとしているのですが、 「ファイルのサイズを調べる」という時点でつまづいてしまっています。。。 適当に英数字を書き連ねたファイル(aaa.txt)を、 (fp = fopen(filename,"a+") という形で開いています。 追加読み書きするので"a+"かな? と思ったのですが、、、   //ファイルの終端に移動し、その位置を取得する   fseek(fp1, 0L, SEEK_END);   position = ftell(fp1);   printf("ファイルポインタの位置は %ld です",position); ファイルの大きさを調べるのに、この方法を試してみたのですが、 位置の値が0になってしまいます。。。 filelength(fileno(fp)); でも試してみましたが、 やっぱりファイルサイズが0と返ってきます。 何が原因なのか、何が悪いのかも判らなくて途方に暮れています。 「ここはこう書くんだよ」と、的確なご回答を戴けると幸いです。 基本的な質問で申し訳ありませんが,宜しくお願い致します。

  • C言語の質問です

    #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { FILE *fpi, *fpo; unsigned char idat; /* 引数のチェック */ if (argc != 3) { fprintf(stderr, "Usage: %s [input] [output]\n", argv[0]); exit(1); } /* 入力画像のオープン */ if((fpi=fopen(argv[1], "rb")) == NULL){ fprintf(stderr, "input file open error\n"); exit(1); } /* 出力画像のオープン */ if((fpo=fopen(argv[2], "wb")) == NULL){ fprintf(stderr, "output file open error\n"); exit(1); } /* 入力画像の読込み */ while (fread(&idat, sizeof(unsigned char), 1, fpi) == 1){ /* 2倍の変換 */ if (idat * 2 > 255) { idat = 255; } else { idat = idat * 2; } /* 変換データの書出し */ if(fwrite(&idat, sizeof(unsigned char), 1, fpo) != 1){ fprintf(stderr, "data write error\n"); exit(1); } } fclose(fpi); fclose(fpo); return (0); } このプログラムをグレースケール化のプログラムに修正してください お願いします

  • ファイル処理について

    大学の課題なのですが、何度取り組んでもエラーになるため、間違いのご指摘、または正答を教えていただけないでしょうか。 問題は以下のものです。 【問題】 ファイルから整数を読み込み,その値によってfpの読み込み位置をかえ,何度目の読み込みで0を読み込んだかを表示するプログラムを作成せよ. ファイルの内容の例 2,4,0, fpの読み込み位置を変えるにはfseekという関数を利用する. fseekの使い方: 現在の読み取り位置xだけずらすには, fseek(ファイルポインタ, x, SEEK_CUR); と記述する. STEP 1 データを一区切りで読み取る("2,"や"-4,"などの整数とコンマの組) 2 読み取ったデータに応じてfpを移動させる("2,"なら2移動,"-4,"なら-4移動) 3 0を読むまで繰り返す +++++*+++++fseek-exercise.c+++++*+++++ #include <stdio.h> int main(void){ FILE *fp; int c; int i; int count=0; char filename[60]; printf("ファイル名 :"); scanf("%s",filename); //ファイルを開く // (* ここに解答を書き加える *) while(1){ printf("読み込み前 : %ld\n",ftell(fp)); //fpから値を読む // (* ここに解答を書き加える *) printf("読み込んだ値は%dです\n",c); printf("読み込み後 : %ld\n\n",ftell(fp)); //読み込み位置をずらす // (* ここに解答を書き加える *) } printf("%d回目です.",count); fclose(fp); return(0); } // 以上になります。 自分で作ったプログラムは以下のようになりました。 #include <stdio.h> int main(void){ FILE *fp; int c; int i; int count=0; char filename[60]; printf("ファイル名 :"); scanf("%s",filename); //ファイルを開く // fp = fopen(filename,"r"); if(fp == NULL){ printf("cannot open\n"); exit(1); } while(1){ printf("読み込み前 : %ld\n",ftell(fp)); //fpから値を読む // c = fgetc(fp); printf("読み込んだ値は%dです\n",c); printf("読み込み後 : %ld\n\n",ftell(fp)); //読み込み位置をずらす // if(c > '0' || c< '9'){ i = atoi(&c); count++; if(i == 0){ break; } fseek(fp,i,SEEK_CUR); } } printf("%d回目です.",count); fclose(fp); return(0); } // どこを訂正していいのかわかりません。 どうぞ、よろしくお願いいたします。

  • c言語で任意のファイルから読み込んだ単語の数をカウントする

    c言語で任意のファイルから読み込んだ単語の数をカウントする 任意のファイルを読み込んだプログラムに、読み込んだ単語の数をカウントするプログラムを追加する課題が出ました。 条件は単語は空白で区切って1単語とする。改行も考慮に入れる。 関数を定義してポインタを使うらしいのですが検討がつきません。 下のプログラムでファイルを読み込むところまでは出来ています。 どうかご教授おねがいします;; #include <stdio.h> int main(int argc, char *argv[]) { FILE *myFile; int i=0,c; if(argc < 2) { fprintf(stderr,"alice.txt is required\n"); return 1; } myFile=fopen(argv[1],"r"); if(myFile==NULL) { fprintf(stderr,"Cannot open; %s\n",argv[1]); } while ((c=fgetc(myFile)) !=EOF) { fprintf(stderr,"%c",c); } fclose(myFile); return 0; }