• ベストアンサー

ファイルから数字列を16進数の数字列として読み込むためには?

C言語初心者です。 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef… 0135792468acebdf0135792468acebdf0135792468acebdf0135792468acebdf… … のようなファイルがあったときに、このファイルを自分で用意した配列num[]に1次元配列形式、かつ16進数で読み込むコードが思いつかず困っています。ファイルは複数行かつ複数列存在し、各行と各列はそれぞれ長さはマチマチとします。 つまり、例えば 1234abcdf 2315a cedf45 のようなファイルをnum[20]という配列に格納したいのです。 この場合、fgetc, fgets, fscanf等、使える関数は数種類あると思うのですが、どの方法がもっとも効率的に、かつ高速に自分が用意した配列numに読み込めるのでしょうか? また、メモリ量もできるだけ節約したいので、同状況でmalloc等で読み込んだファイルの文字数のみの要素数を確保するやり方も考えているのですが、これも思いつかずに困っています(つまり、num[10000]とかをやるのは避けたいということです) C言語に堪能な方がおられましたらご教授下さい。具体的なソースで説明していただけると助かります。

  • g47040
  • お礼率55% (125/226)

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

  • ベストアンサー
noname#144013
noname#144013
回答No.4

こんにちは。 #3の方が書かれているように、 C言語でファイルサイズを取得する方法はいくつかあると思います。 ◎参考サイト C言語 ファイル位置の取得 ファイルサイズの取得 http://simd.jugem.jp/?eid=57 fseek、および ftell によるファイルサイズを求める方法を用いて、今回の案件の処理を行う サンプルプログラムを作ってみました。(下記参照) ファイル読込みには、fgets を使用しています。 なお、今回のサンプルで扱えるファイルのファイルサイズ上限は、2GByteまでとなります。 注)間違っている点があるかもしれませんので、あくまで参考程度として下さい。 ■サンプルプログラム 実行の際は、コマンドライン引数に、入力ファイル名(16進文字列が記述されたテキストファイル のファイル名)を指定して実行して下さい。 注)以下のソースには、インデント(字下げ)のため、全角スペースが入っています。   コンパイルの際は、半角スペースorタブへ置換するか削除して下さい。 --------------------------------------------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> /* define const */ #define MAXBUF 256 /* prototype */ int ConvHex2Bin( char cHex, unsigned char *cBin ); void DumpBinBuf( unsigned char *pBinBuf, long nBinLen ); /* * main */ int main( int argc, char *argv[] ) {   char *psFileName;   FILE *fpInp;   int ists;   int ierr;   int i, len;   long nGetSize;   long nPos, nBinLen;   char cch;   unsigned char bch;   unsigned char *pBinBuf;   char strLine[MAXBUF+1];   //コマンドラインのチェック   if(argc < 2){     printf( "## Command Line Error!! (Not File Name)\n");     return -1;   }   psFileName = argv[1];   //入力ファイル名の表示   printf( "Input File Name: \"%s\"\n", psFileName );   //入力ファイルオープン   if((fpInp = fopen( psFileName, "rb" )) == NULL){     printf( "## File Open Error!!\n" );     return 1;   }   //入力ファイルのサイズを取得   //※ファイル末尾へシークしてファイルポジションを取得   ists = fseek( fpInp, 0L, SEEK_END );   if(ists){     printf( "## File Seek Error (SEEK_END)!!\n" );     fclose( fpInp );     return 2;   }   nGetSize = ftell( fpInp );   if( nGetSize == -1L ){     printf( "## File Point Error ! (ftell)!\n" );     fclose( fpInp );     return 3;   }   //入力ファイルのサイズ表示   printf( "Input File Size: %ld (byte)\n", nGetSize );   //ファイルポジションを先頭に移動   ists = fseek( fpInp, 0L, SEEK_SET );   if(ists){     printf( "## File Seek Error!! (SEEK_SET)\n" );     fclose( fpInp );     return 2;   }   //バイナリバッファのメモリ確保   pBinBuf = (unsigned char *)malloc( nGetSize );   if(pBinBuf == NULL){     printf( "## Memory Allocate Error!!\n" ) ;     fclose( fpInp );     return 4;   }   //ファイル読込み&バイナリバッファへ格納   nPos = 0;   ierr = 0;   while( fgets( strLine, MAXBUF, fpInp ) != NULL )  //1行読込み   {     //1行毎に読込んでバイナリバッファへ格納     len = strlen( strLine );     for( i=0; i<len; i++ )     {       cch = strLine[i];       if(cch != '\r' && cch != '\n'){      //改行文字以外?         ists = ConvHex2Bin( cch, &bch );  //HEX文字→バイナリ値変換         if(ists == 0){           pBinBuf[nPos++] = bch;      //バイナリバッファへセット         }         else {           ierr = 1;           break;    //エラーならループを抜ける         }       }     }     if(ierr) break;    //エラーならループを抜ける   }   nBinLen = nPos;    //読込んだデータ長を取得   //ファイルクローズ   fclose( fpInp );   //16進文字以外が有った場合のエラー表示   if(ierr){     printf( "## File format Error!!\n" );     if(pBinBuf != NULL){       free( pBinBuf );       pBinBuf = NULL;     }     return 5;   }   //バイナリデータのデータ長を表示   printf( "Bin data Length: %ld (byte)\n", nBinLen );   //バイナリデータのダンプ   printf( "** Binary data Dump **\n" );   DumpBinBuf( pBinBuf, nBinLen );   //確保したメモリの解放   if(pBinBuf != NULL){     free( pBinBuf );     pBinBuf = NULL;   }   return 0; } /* * ConvHex2Bin : HEX文字→バイナリ値変換 */ int ConvHex2Bin( char cHex, unsigned char *cBin ) {   *cBin = 0;   if(cHex >= '0' && cHex <= '9'){     *cBin = cHex - '0';   }   else if(cHex >= 'a' && cHex <= 'f'){     *cBin = cHex - 'a' + 10;   }   else if(cHex >= 'A' && cHex <= 'F'){     *cBin = cHex - 'A' + 10;   }   else {     *cBin = '.';     return -1;   }   return 0; } /* * DumpBinBuf : バイナリデータのダンプ */ void DumpBinBuf( unsigned char *pBinBuf, long nBinLen ) {   long nPos;   unsigned char bch;   printf("Address : +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F\n");   for(nPos=0; nPos<nBinLen; nPos++)   {     if((nPos % 16L) == 0){       printf("%08lx:", nPos);     }     bch = pBinBuf[nPos];     if(bch >= 0 && bch < 16){       printf(" %1x", bch);     }     else {       printf(" .");     }     if( (nPos+1) == nBinLen || ((nPos+1) % 16L) == 0 ){       printf("\n");     }   } } --------------------------------------------------------- ■プログラムの実行例 ◎入力ファイル("test.txt")が下記内容だった場合 1234abcdf 2315a cedf45 ◎実行結果 Input File Name: "test.txt" Input File Size: 26 (byte) Bin data Length: 20 (byte) ** Binary data Dump ** Address : +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F 00000000: 1 2 3 4 a b c d f 2 3 1 5 a c e 00000010: d f 4 5

g47040
質問者

お礼

うおおおお! こんなにも長いソースまで載せて頂いて、感謝感激です。 是非参考にさせていただきますね。

その他の回答 (4)

noname#144013
noname#144013
回答No.5

こんにちは。 連投すみません。#4で回答したFarEyesです。 前述の#4の回答で補足があります。 サンプルソース内のバイナリデータダンプ用の関数 DumpBinBuf の下記部分については、 コメントで記述している内容となっています。 (HTML上だと、連続した半角スペースは、1個のスペースになってしまうのを忘れていました。)   関数 DumpBinBuf より抜粋   -------------------------   bch = pBinBuf[nPos];   if(bch >= 0 && bch < 16){     printf(" %1x", bch);  //←書式文字列(”…”)内のスペース文字の数は2個です。   }   else {     printf(" .");       //←書式文字列(”…”)内のスペース文字の数は2個です。   }   ------------------------- お手数ですが、Cソースとしてご利用の際は修正願います。m(__)m

  • php504
  • ベストアンサー率42% (926/2160)
回答No.3

stat( )関数が使えればファイルサイズはわかりますよ あるいはファイル終端までシークしてftell( )関数でもわかります #include <stdio.h> #include <sys/stat.h> int main(void) { FILE* fp; struct stat st; stat("test.txt", &st); printf("stat size=%d\n", st.st_size); fp = fopen("test.txt", "r"); fseek(fp, 0, SEEK_END); printf("ftell size=%d\n", ftell(fp)); fclose(fp); return 0; }

g47040
質問者

お礼

回答有難うございます。なるほど、fseekってのがあるんですか。 これでファイルサイズを確保できそうですね。 有難うございました!

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.2

>1234abcdf >2315a >cedf45 >のようなファイルをnum[20]という配列に格納したいのです。 改行を除くと20文字ありますね。各文字を16進数とみなして、 char型やint型などにした値(0~15のいずれか)をnum[0]~num[19]に 格納したい、ということでしょうか? ファイルのサイズを求めて、その分だけの領域をmallocで確保するのが よいと思いますが、その、動的に確保する領域に、改行の分を含むのはまずいですか? もし、まずくないのであれば、上の例だと、 1)改行を含むファイルのサイズ分の領域を動的に確保する。 2)以下2項を、ファイルが尽きるまで繰り返す。 3)ファイルから1文字読む。 4)読んだ1文字が16進数とみなせるなら、動的確保領域に格納する。 この場合、動的確保領域はchar型やint型などの22個分とか23個分とかで、 そのうち実際に使っているのは20個分となり、改行の数だけ領域がムダになります。 >#1さん >それと Integer, Long それぞれ、なんバイトなのでしょう ? 別に何バイトでもいいのではありませんか?

g47040
質問者

補足

早速の回答ありがとうございます。 おっしゃられるように、 unsigned char num[20]; とし、num[0]からnum[19]に格納するという意味で書いておりました。 舌足らずで申し訳ありませんでした。 改行コードの分を含んでいても一向に構いません。ファイルは様々な容量のものを読み込みたいのですが、そのファイルは人間が毎回自由に入力するようにしたいと思っていて、ファイルサイズを事前把握して配列をmallocで動的確保というのは(たぶん)難しいのではないかと考えているのです。ファイルサイズを取得するAPI等がC言語には存在しないようで、ガバッと動的確保、というのは不可能ではということです。 今考えているのは「fgetcで一文字ずつ読み込んで、もし格納する文字なら文字コードをatoi変換し、mallocで1文字確保し、格納」を文字数分繰り返す、ということを考えているのですが、何か愚の骨頂のような気がしてなりません。それだと処理が重くて、プログラム的にも繁雑過ぎる気がすするのです。 でもそれしかないんですかね…。 何かしらお気づきの点あれば、教えていただきたく思います。

  • A88No8
  • ベストアンサー率52% (834/1602)
回答No.1

こんにちは >num[20]という配列に格納したい  この配列のデータ型は、Integer それとも Long ?  それと Integer, Long それぞれ、なんバイトなのでしょう ?  すべてはそこから始まると思います。

g47040
質問者

お礼

早速の回答ありがとうございます。 16進数のデータを格納したいので、結論から申し上げると、配列numのデータ型はunsigned charを予定しています。

関連するQ&A

  • 文字列ファイル表示プログラム

    Cプログラムの問題なのですが、 「複数の文字からなるファイルを読み込み、全て読み込んだ後に表示するプログラムを作成したいと思います。あらかじめ文字列を配列として宣言するのではなく、文字列の長さを確認した上で、malloc()標準関数でメモリを割り当て、表示しなさい」 文字列を宣言せずに、どうやったらファイルの中の文字列を読み込むことができるのでしょうか?その上、文字列の長さを確認したあとmalloc関数を使うにはどうすればいいのでしょうか。 どなたか教えてください。お願い致します。

  • ファイル内の文字列を配列へ

    テキストファイルの中の文字列を1字づつ配列に入れたいのですが、どのように書けばよいでしょうか? while((c=fgetc(fp)) ! = EOF){ c=rv[i];   i++; } 一応このようにやってみたのですが、ダメなようです。

  • C言語での、テキストファイル読み込みについて

    30行×30列の数字が記載されてテキストファイルがあり、 それをC言語で例えばA[ ][ ]といった配列に読み込みたいのですが、 どうすればよいでしょうか? fscanf関数を用いればできないことはないと思うのですが、 30個も書かなければならないのは冗長なので・・・。 なにかよい方法はありますでしょうか?

  • ファイルから16進数文字列として読み込めない

    例えば 2b7e151628aed2a6abf7158809cf4f3c のうようなファイルを 2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c ------(*) のような16個の配列からなる数字列として考えて読み込むにはどうすればよいでしょうか?うまく思いつきません。 なお、 unsigned char num[16]; (上記の数字列を入力) for(int i=0; i<16; i++) printf("%x", num[i]); を実行すると(*)のように表示されるようにしたいです。 詳しい方よろしくお願いします。

  • fgetsでエラー

     C言語を勉強しているものです。 fgetsやfscanfで第一引数にダブルポインタ変数を用いるとエラーが出ます。 なぜそうなるんでしょうか? ダブルポインタだと不可能なんでしょうか? char **moji; FILE *fp; moji = (char**)malloc(sizeof(char*) * 128 * 256); fp = fopen("test.txt","r+"); fgets(moji[0], 26, fp); //ここでエラー mojiを[0]で指定してあげてもエラーなんでしょうか? fgetsだけでなくfscanfでもそうです。 どうしてそうなんるんでしょう?

  • 小数が、18行x72列、並んでいるファイルを読みます。どうしたら良いでしょうか。

    ・小数が、72列18行あります。 ・小数は、ときに負号が記載されていることがあります。 ・小数は、C言語でfprintfで、"%-7.5f " で打ち出したもので、見てみると、7桁、時に八桁のようですが。 ・区切りは、上記のように空白一つで、fprintfで書き出したはずなのですが、実際には、スペースが一つか二つ(不定。どうやら負号のない場合には空白が一つ増える・・・ということみたいですが不明)入っています。 ・72列のデータの後はすぐに改行しておらず、スペースが一つか二つはいって、改行しています。 自分で書き出したデータなのに間抜けなことですが、どうしたらC言語で安定して、読み込めるでしょうか。 fgets? fgetc? scanfは不安です。 windows2002, thinkpad, borland C v5.5無料版です。 どうぞよろしくお願いいたします。

  • C言語の入出力関数の違い

    C言語を勉強しています。 自分が知っている入出力を行う関数は printf scanf fprintf fscanf sprintf sscanf gets puts getc putc fgets fputs fgetc fputc なのですがそれぞれのメリット・デメリットの違いがまだあいまいです。 というかまだ上の4つくらいしかまともに使ったことがありません。 自分の考えでは、 ・上の4つは書式を指定でき、ファイルから読み込みするときなどは fgetsよりもfscanfが使いやすいと思っています。 ・スペースを読み込みたいときはscanfではなくgetsを使わなければならない。 ・システム開発でscanfを使うことは危険なのでほとんどない。 間違った考え方をしていたり、どのようなデータのときに どの関数を使うのが正しい、常識、と知っている方いらっしゃいましたら 教えてください。

  • mallocの確保要素数の限界は?

    C言語では、確か確保できる要素数が65536個までとか聞いたことがあります。ためしに、 #include <stdio.h> #define NUM 100000 int main(void){ unsigned char x[NUM]; x[NUM-1] = 200; printf("%d", x[NUM-1]); } のようなプログラムを作って、gccでコンパイルしたところ、問題なく動きました。しかし、処理系によっては動かないこともありえると思うのですが、私の理解は正しいでしょうか?もちろん、PCに装備しているメモリ容量以下、という前提です。 つまり、配列の要素数を65536個を超えて確保し、要素も65537以上の要素で指定したいのです。2次元でx[2][65536]とかやると、65537個の配列とかが作れないので、2次元確保はしたくないのです。mallocなら処理系に関係なく65536要素を超える配列の設定が可能なのでしょうか? この辺の知識があいまいなので、はっきり知っている方がいたら教えてもらえると助かります。 よろしくお願いします。 main文内で

  • fgetcで全角文字列の読み込み

    ファイルから一文字ずつ全角文字を読み込んで、それを配列を格納する場合、C言語では、どのようなコードになるのですか? 一バイトの場合は簡単ですが、2バイトは分かりません。 fgetcはint型を返すみたいです。 教えて下さい。お願いいたします。

  • ビン数が大きなヒストグラム

    C言語で、あるデータ群をヒストグラムにまとめたいと思っています。 ヒストグラムのビンに相当するものを配列で用意して…… という、よく学習課題でもあるような形ならば簡単に出来るのですが、 今回やりたいと思っているものは、非常にビンの数が多いのです。 具体的に言うと、数万、数十万という数で、 それだけの要素数を持った配列を用意しようと思っても メモリの都合上、エラーが起きてしまいます。 そこで、配列に記憶させるのではなく、 テキストファイルを用意して、これの入出力をすることで ヒストグラム情報を作る方法をとりました。 この方法で成功はしたのですが、ファイルのオープン/クローズを 何度も繰り返すことになるため、動作が非常に遅くなってしまいます。 要するに、数万、数十万という数の変数を扱いたい、という問題なのですが、 何か良い手段はないでしょうか。

    • 締切済み
    • CGI

専門家に質問してみよう