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); 
}

我怎样才能将其更改为有效的方式?

4

3 回答 3

2

当您可以使用自定义格式轻松读取整个结构并让字段自动填充正确的值时,问题是什么?

struct MyDataFormat {
  double d;
  int8 i1;
  float32 f[6];
  ..
};

MyDataFormat buffer;

fread(&buffer, sizeof(MyDataFormat), 1, file);
于 2012-04-16T16:55:54.600 回答
1

除了文件的“结构”之外,我们还需要知道所涉及的数据类型的格式,以及如果格式不是文本格式,您所说的“行”是什么意思。然而,一般来说,您将 1) 必须读取一个适当大小的块,然后根据指定的格式从中提取每个值。对于整数值,使用移位提取无符号整数值相当容易;对于int8,实际上,您只需要读取字节即可。对于大多数机器,只需将无符号整数转换为相应大小的有符号类型即可,尽管这并不能明确保证;如果unsigned char 大于CHAR_MAX,则必须按比例缩小以获得
适当的值:类似的东西-(UCHAR_MAX+1 - value)应该可以解决问题(对于chars——对于较大的类型,您还必须担心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;
}

. 甚至用于memcpy从 a 复制uint64_t到 adouble中。)

如果您的机器不使用 IEEE 浮点,并且外部格式是 IEEE,您必须将 8 字节字作为 8 字节 unsigned int ( unsigned long long),然后根据 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;
}

但是,在确定需要它之前不要这样做。

于 2012-04-16T17:51:11.440 回答
1

如果每一行都是相同的格式,我可能会一次将一行读入缓冲区,然后有一个函数将该缓冲区分成单独的元素 - 更容易理解,更容易测试,适用于更大的文件并且可能更有效做更少的读取。

于 2012-04-16T16:55:07.760 回答