
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で質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# pythonのファイルの並びでの読み込みとリストについて 4 2022/04/13 03:52
- C言語・C++・C# 至急お願いします。C言語で.imgのファイルを読み込んで1バイトづつ出力するプログラムを作りたいので 3 2023/01/16 22:49
- Ruby 初心者プログラミング 3 2022/10/12 11:31
- C言語・C++・C# C言語初心者 ポインタについて、お助けください、、 2 2023/03/15 23:50
- その他(プログラミング・Web制作) Pythonで、データファイルと列名ファイルを1つのファイルにしたいです。 1 2023/07/27 20:29
- その他(プログラミング・Web制作) pythonでクラスで複数のメソッドを利用する方法 2 2022/04/15 04:17
- Perl perlで2次元配列をサブルーチンに値渡しで渡す 5 2022/12/17 18:49
- Excel(エクセル) 配列操作について 5 2023/04/18 07:27
- Excel(エクセル) PowerQueryに詳しい方教えてください(Office365) 1 2022/07/24 21:11
- Visual Basic(VBA) 【VBA】写真の縦横比を変えずに貼り付ける 5 2023/06/13 11:42
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Enterキーを押されたら次の処理...
-
C++で指定文字列のカウント方法...
-
#defineが使用するメモリ領域に...
-
プログラミングの授業の課題です
-
文字列の探索
-
Cプログラムについて
-
エラーについて質問です。
-
【C言語】全角文字の配列を、全...
-
空白を含んだ文字列がうまく格...
-
C言語で複数列のデータを1列の...
-
C言語でのCSVファイルの読み出...
-
エクセルで可視セルにのみ値貼...
-
2÷3などの余りについて
-
*をユーザーが入力した数字の数...
-
Aの値からBの値を除するとは??
-
C言語を実行すると-infが出てき...
-
「指定されたキャストは有効で...
-
値差の%計算方法について
-
「Aに対するBの割合」と「Aに対...
-
C言語での引数の省略方法
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
Enterキーを押されたら次の処理...
-
【C言語】全角文字の配列を、全...
-
#defineが使用するメモリ領域に...
-
C言語で複数列のデータを1列の...
-
printf による16進表示について
-
char型2つを結合し、short型に...
-
空白を含んだ文字列がうまく格...
-
矢印キーを押下してコンソール...
-
C++で指定文字列のカウント方法...
-
コマンドファイルから、ビット...
-
C言語のプログラムで、途中で止...
-
終了条件Ctrl+zについて,結果表...
-
配列への文字列の格納について
-
困ってます!書き方がわかりま...
-
ファイルから数字列を16進数の...
-
fread(),fwrite()等について
-
0x8, スペース, 0x8をプログラ...
-
static付き宣言の初期化
-
構造体メンバの初期化
-
c言語で文書を読み込み、単語の...
おすすめ情報