C言語初心者です。
0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef…
0135792468acebdf0135792468acebdf0135792468acebdf0135792468acebdf…
…
のようなファイルがあったときに、このファイルを自分で用意した配列num[]に1次元配列形式、かつ16進数で読み込むコードが思いつかず困っています。ファイルは複数行かつ複数列存在し、各行と各列はそれぞれ長さはマチマチとします。
つまり、例えば
1234abcdf
2315a
cedf45
のようなファイルをnum[20]という配列に格納したいのです。
この場合、fgetc, fgets, fscanf等、使える関数は数種類あると思うのですが、どの方法がもっとも効率的に、かつ高速に自分が用意した配列numに読み込めるのでしょうか?
また、メモリ量もできるだけ節約したいので、同状況でmalloc等で読み込んだファイルの文字数のみの要素数を確保するやり方も考えているのですが、これも思いつかずに困っています(つまり、num[10000]とかをやるのは避けたいということです)
C言語に堪能な方がおられましたらご教授下さい。具体的なソースで説明していただけると助かります。
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
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
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;
}
回答有難うございます。なるほど、fseekってのがあるんですか。
これでファイルサイズを確保できそうですね。
有難うございました!
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 それぞれ、なんバイトなのでしょう ?
別に何バイトでもいいのではありませんか?
この回答への補足
早速の回答ありがとうございます。
おっしゃられるように、
unsigned char num[20];
とし、num[0]からnum[19]に格納するという意味で書いておりました。
舌足らずで申し訳ありませんでした。
改行コードの分を含んでいても一向に構いません。ファイルは様々な容量のものを読み込みたいのですが、そのファイルは人間が毎回自由に入力するようにしたいと思っていて、ファイルサイズを事前把握して配列をmallocで動的確保というのは(たぶん)難しいのではないかと考えているのです。ファイルサイズを取得するAPI等がC言語には存在しないようで、ガバッと動的確保、というのは不可能ではということです。
今考えているのは「fgetcで一文字ずつ読み込んで、もし格納する文字なら文字コードをatoi変換し、mallocで1文字確保し、格納」を文字数分繰り返す、ということを考えているのですが、何か愚の骨頂のような気がしてなりません。それだと処理が重くて、プログラム的にも繁雑過ぎる気がすするのです。
でもそれしかないんですかね…。
何かしらお気づきの点あれば、教えていただきたく思います。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・人生のプチ美学を教えてください!!
- ・10秒目をつむったら…
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・【大喜利】【投稿~9/18】 おとぎ話『桃太郎』の知られざるエピソード
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Enterキーを押されたら次の処理...
-
C言語で複数列のデータを1列の...
-
空白を含んだ文字列がうまく格...
-
処理系によって。
-
プログラムC
-
終了条件Ctrl+zについて,結果表...
-
#defineが使用するメモリ領域に...
-
setjmp・longjmpに関しまして
-
【C言語】全角文字の配列を、全...
-
static付き宣言の初期化
-
reallocでエラー
-
csvファイルの読み取り
-
2÷3などの余りについて
-
信頼区間の1.96や1.65ってどこ...
-
std::set<int> で、ある値が何...
-
「Aに対するBの割合」と「Aに対...
-
#define _CRT_SECURE_NO_WARNIN...
-
【gcc・cygwin】multiple defin...
-
ある商品のロス率を5%見込み、...
-
Aの値からBの値を除するとは??
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
Enterキーを押されたら次の処理...
-
printf による16進表示について
-
#defineが使用するメモリ領域に...
-
C言語で複数列のデータを1列の...
-
C言語のプログラムで、途中で止...
-
reallocでエラー
-
【C言語】全角文字の配列を、全...
-
構造体メンバの初期化
-
char型2つを結合し、short型に...
-
空白を含んだ文字列がうまく格...
-
VC++でSQLへSELECT文を送ったの...
-
C言語でのCSVファイルの読み出...
-
エラーについて質問です。
-
setjmp・longjmpに関しまして
-
矢印キーを押下してコンソール...
-
タイムカードのシステムを作り...
-
大量のファイルを読み込み、そ...
-
C言語、ファイル操作、fgets()...
-
Cでファイルの行数をカウントす...
-
リストの作成と出力(C言語)
おすすめ情報