2012-04-16 14 views
1

私はC++で始まり、バイナリファイルを読む必要があります。行の異なるデータ型を読む++

'double';'int8';'float32';'float32';'float32';'float32';'float32';'float32';'int8';'float32';'float32';'float32';'float32';'int8';'float32' 

またはバイト数の:

私は、ファイルの構造を知るには、すなわち、各ファイルの行がで構成されて

8 1 4 4 4 4 4 4 1 4 4 4 4 1 4 

私はいくつかのコードをしたが、あまりにも廃止されました...ここ はコードです:

void test1() { 
const char *filePath = "C:\20110527_phantom19.elm2";  
double *doub;   
int *in; 
float *fl; 
FILE *file = NULL;  
unsigned char buffer; 

if ((file = fopen(filePath, "rb")) == NULL) 
    cout << "Could not open specified file" << endl; 
else 
    cout << "File opened successfully" << endl; 

// Get the size of the file in bytes 
long fileSize = getFileSize(file); 
cout << "Tamanho do ficheiro: " << fileSize; 
cout << "\n"; 
// Allocate space in the buffer for the whole file 
doub = new double[1]; 
in = new int[1]; 
fl = new float[1]; 
// Read the file in to the buffer 
//fread(fileBuf, fileSize, 1, file); 

//fscanf(file, "%g %d %g", doub[0],in[0],fl[0]); 

fread(doub, 8, 1, file); 
//cout << doub[0]<< " "; 
fseek (file ,8, SEEK_SET); 
fread(&buffer,1,1,file); 
//printf("%d ",buffer); 
fread(fl,4,1,file); 
//cout << fl[0]<< " "; 
fread(fl,4,1,file); 
//cout << fl[0]<< " "; 
fread(fl,4,1,file); 
//cout << fl[0]<< " "; 
fread(fl,4,1,file); 
//cout << fl[0]<< " "; 
fread(fl,4,1,file); 
//cout << fl[0]<< " "; 
fread(fl,4,1,file); 
//cout << fl[0]<< " "; 
fread(&buffer,1,1,file); 
//printf("%d ",buffer); 
fread(fl,4,1,file); 
//cout << fl[0]<< " "; 
fread(fl,4,1,file); 
//cout << fl[0]<< " "; 
fread(fl,4,1,file); 
//cout << fl[0]<< " "; 
fread(fl,4,1,file); 
//cout << fl[0]<< " "; 
fread(&buffer,1,1,file); 
//printf("%d ",buffer); 
fread(fl,4,1,file); 
//cout << fl[0]<< "\n"; 

cin.get(); 
//delete[]fileBuf; 
fclose(file); 
} 

がどのように私はこれが効率的な方法に変更できますか?

+0

あなたはそれがバイナリファイルだと言います。どのように行を区切っていますか? – jrok

+0

実際、ファイルには行がありません。ファイルそのものは、同一のサンダードバイト列、すなわち8 1 4 4 4 4 4 4 1 4 4 4 4 1 4 8 1 4 4 4 4 4 4 1 4 4 4 4 1 4 ... – luiserta

答えて

1

各行が同じ形式の場合は、一度に1行ずつバッファに読み込んでから別の要素にそのバッファを分けた関数を用意しています。より少ない読み取りを効率的に行うことができます。

+0

データはバイナリ形式ですが、 'getline'は動作しません。 –

+0

@JamesKanze - 行を読むことはgetl​​ine()を意味しません。ちょうど1バイト、次に4バイトを読むのは非効率ですが、4GBのファイル全体をメモリに読み込んでスワップアウトすることも悪いことです。一度にブロック(すなわちライン)を読むことは、この場合、簡単な解決策である。何よりもmmap()ですが、私は物事を混乱させたくありませんでした。 –

+0

固定サイズのブロックを読むことは良いことです。行の概念が存在しない場合、行を読むことは悪いです。 (もちろん、 'FILE *'と 'std :: istream'の両方がバッファリングの問題を処理します) –

2

カスタムフォーマットの構造体全体を簡単に読み込み、フィールドに自動的に正しい値が入力されている場合には、どのような問題がありますか?フォーマット がテキスト形式でない場合は、

ファイルの「構造」に加えて
struct MyDataFormat { 
    double d; 
    int8 i1; 
    float32 f[6]; 
    .. 
}; 

MyDataFormat buffer; 

fread(&buffer, sizeof(MyDataFormat), 1, file); 
+0

+1、彼はおそらく、 '__attribute __((packed))'のようないくつかの "pack"ディレクティブを使うべきです。 – mfontanini

+1

またはC++ 11の '[[packed]]'です。 –

+0

これは問題ではありません。与えられたプラットフォーム上の特定のコンパイラの特定のバージョンでうまく動作するかもしれませんが、一般的にはうまく動作しません。 –

1

、我々は、関連するデータの種類のフォーマット を知っておく必要があり、あなたは「行」とはどういう意味します。しかし、一般的には、1)適切なサイズのブロック を読み取ってから、指定されたフォーマットに従って からそれぞれの値を抽出する必要があります。積分値については、シフトを使用して符号なし積分値を抽出することは容易に です。 int8の場合、 の場合、実際にはバイトを読み取るだけです。ほとんどのマシンでは、 符号なし整数を対応するサイズの符号付きタイプにキャストすると になりますが、これは明示的に保証されていません。 unsigned charCHAR_MAXより大きい場合、あなたは
適切な値を得るためにそれを縮小する必要があります:-(UCHAR_MAX+1 - value)ようなものが トリックを行う必要があります(charの大きなタイプのため—のために、あなたはまた、約 心配する必要はあり実際にはUINT_MAX+1がオーバーフローします)。

外部フォーマットはIEEEあり、そしてそれはまた、どのようなあなたのマシン の使用(WindowsとUnixマシンのための通常の場合、まれにメインフレーム用ケース )ですが、その後、あなたは符号なしの4または8バイトを読み取ることができの場合整数 (再び、シフトを使用)、およびそれをしゃれ入力し、のようなもの:

uint64_t 
get64BitUInt(char const* buffer) 
{ 
    return reinterpret_cast<double>(
      ((buffer[0] << 52) & 0xFF) 
     | ((buffer[1] << 48) & 0xFF) 
     | ((buffer[2] << 40) & 0xFF) 
     | ((buffer[3] << 32) & 0xFF) 
     | ((buffer[4] << 24) & 0xFF) 
     | ((buffer[5] << 16) & 0xFF) 
     | ((buffer[6] << 8) & 0xFF) 
     | ((buffer[7]  ) & 0xFF)); 
} 

double 
getDouble(char const* buffer) 
{ 
    uint64_t retval = get64BitUInt(buffer); 
    return *reinterpret_cast<double*>(&retval); 
} 

(これは、通常のネットワークバイトオーダーに対応し、あなたのバイナリ形式 は別の規則を使用している場合、あなたはそれを適応させる必要があります。そして、 reinterpret_castは実装に依存します定義された動作;

double 
getDouble(char const* buffer) 
{ 
    union 
    { 
     double   d; 
     uint64_t  i; 
    }    results; 
    results.i = get64BitUInt(buffer); 
    return results.d; 
} 

:あなたは としてそれを書き換える必要があるかもしれません。あるいは、uint64_tからdoubleにコピーするのにmemcpyを使用することさえできます。あなたのマシンがIEEE浮動小数点を使用しません。また、外部形式 がIEEEの場合)

、あなたは記号を抽出し、その後、8バイトの符号なし int型(unsigned long long)として8バイトワードをピックアップする必要があります、指数および仮数 IEEE形式による。次のようなもの:あなたはしかし、それをする必要があります確信しているまで

double 
getDouble(char const* buffer) 
{ 
    uint64_t   tmp(get64BitUInt(buffer); 
    double    f = 0.0 ; 
    if ((tmp & 0x7FFFFFFFFFFFFFFF) != 0) { 
     f = ldexp(((tmp & 0x000FFFFFFFFFFFFF) | 0x0010000000000000), 
        (int)((tmp & 0x7FF0000000000000) >> 52) - 1022 - 53) ; 
    } 
    if ((tmp & 0x8000000000000000) != 0) { 
     f = -f ; 
    } 
    return f; 
} 

はこれをしないでください。

+0

実際、ファイルには行がありません。ファイルそのものは同じサンダードバイト列、すなわち8 1 4 4 4 4 4 4 1 4 4 4 4 1 4 8 1 4 4 4 4 4 4 4 4 4 4 1 4 ... – luiserta

+0

@luisertaそれが私が理解したものです。バイナリ形式のように聞こえ、バイナリ形式には "行"は含まれません(C++では '\ n 'で終わる印字可能な文字列として定義されています)。 **このファイルで 'getline'を使わないでください**。 floatまたはdoubleのバイトの1つは ''\ n' 'のように見えます。ブロックの正確なバイト数に 'std :: istream :: read'を使い、上記のように解析します。 (そして、私の最初のバージョンの 'getDouble'にはエラーがあります。これは編集で修正します)。 –

関連する問題