アプリ版:「スタンプのみでお礼する」機能のリリースについて

C言語初心者です。
0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef…
0135792468acebdf0135792468acebdf0135792468acebdf0135792468acebdf…

のようなファイルがあったときに、このファイルを自分で用意した配列num[]に1次元配列形式、かつ16進数で読み込むコードが思いつかず困っています。ファイルは複数行かつ複数列存在し、各行と各列はそれぞれ長さはマチマチとします。
つまり、例えば
1234abcdf
2315a
cedf45
のようなファイルをnum[20]という配列に格納したいのです。

この場合、fgetc, fgets, fscanf等、使える関数は数種類あると思うのですが、どの方法がもっとも効率的に、かつ高速に自分が用意した配列numに読み込めるのでしょうか?

また、メモリ量もできるだけ節約したいので、同状況でmalloc等で読み込んだファイルの文字数のみの要素数を確保するやり方も考えているのですが、これも思いつかずに困っています(つまり、num[10000]とかをやるのは避けたいということです)

C言語に堪能な方がおられましたらご教授下さい。具体的なソースで説明していただけると助かります。

A 回答 (5件)

こんにちは。



#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
    • good
    • 0
この回答へのお礼

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

お礼日時:2009/03/18 08:17

こんにちは。


連投すみません。#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
    • good
    • 0

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;
}
    • good
    • 0
この回答へのお礼

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

お礼日時:2009/03/18 08:14

>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 それぞれ、なんバイトなのでしょう ?

別に何バイトでもいいのではありませんか?

この回答への補足

早速の回答ありがとうございます。
おっしゃられるように、
unsigned char num[20];
とし、num[0]からnum[19]に格納するという意味で書いておりました。
舌足らずで申し訳ありませんでした。
改行コードの分を含んでいても一向に構いません。ファイルは様々な容量のものを読み込みたいのですが、そのファイルは人間が毎回自由に入力するようにしたいと思っていて、ファイルサイズを事前把握して配列をmallocで動的確保というのは(たぶん)難しいのではないかと考えているのです。ファイルサイズを取得するAPI等がC言語には存在しないようで、ガバッと動的確保、というのは不可能ではということです。

今考えているのは「fgetcで一文字ずつ読み込んで、もし格納する文字なら文字コードをatoi変換し、mallocで1文字確保し、格納」を文字数分繰り返す、ということを考えているのですが、何か愚の骨頂のような気がしてなりません。それだと処理が重くて、プログラム的にも繁雑過ぎる気がすするのです。

でもそれしかないんですかね…。
何かしらお気づきの点あれば、教えていただきたく思います。

補足日時:2009/03/17 02:00
    • good
    • 0

こんにちは


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

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

お礼日時:2009/03/17 01:56

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