文件打开及数据存储方式
文件打开方式:
member constant | stands for | access |
---|---|---|
std::ios::in | input | 读 |
std::ios::out | output | 写 |
std::ios::binary | binary | 指令以二进制方式执行,而非文本 |
std::ios::ate | at end | 在文件尾打开 |
std::ios::app | append | 所有的输出操作在文件尾 |
std::ios:: | truncate | 丢弃打开前的内容 |
读取一个文件首先需要知道文件的组织方式,是文本还是二进制,否则后续读取会出问题。
以二进制方式读取时, 需要知道数据的组织方式,数据描述是什么。有多少数据、数据类型是什么、怎么排布的等,一般数据集都有附带的数据描述。
从二进制文件中读取数据存储到vector中,一开始的做法是先用malloc分配预定大小给指针,然后将数据构造进vector。
size_t dataSize = number*sizeof(float);
float* tmp = (float*)malloc(dataSize);
fin.seekg(offset, std::ios::beg).read((char*)tmp, dataSize);
pbsdf->M = vector<float>(tmp, tmp + number);
后来发现可以直接将数据写入vector,改成以下形式,免去了vector的复制操作:
struct pbsdf{
std::vector<float> M;
}
pbsdf->M.resize(number);
fin.seekg(offset, std::ios::beg).read((char*)pbsdf->M.data(), number * sizeof(float));
对于string类型也可以如法炮制。
文件指针及tellg、seekg函数
文件操作中读取指针的位置十分重要,可以使用tellg()、seekg()函数来获取当前读取位置、偏移读取指针。
使用上述函数能够简单地计算文件的大小:
std::streampos curPos = fin.tellg();
fin.seekg(0, std::ios::end);
float filesize = fin.tellg() / (1024.0f*1024.0f); // 956623530 / (1024*1024)
printf("Loading tensor data from \"%s\" .. (%lf MB, %i field%s)\n",
filePath.string().c_str(), filesize, n_fields, n_fields > 1 ? "s" : "");
fin.seekg(curPos, std::ios::beg);
seekg函数使用时有一个注意点,在C++98标准中,如果在调用seekg函数前ifstream的eofbit没有被清除,那么调用会失败。在C++11标准以后行为发生变化,如果在调用seekg前设置了eofbit,那么seekg会清除eofbit。
//C++98
//If the eofbit flag is set before the call,
//the function fails (sets failbit and returns).
//如果文件已经结束,再次调用seekg前要先clear
fin.clear();
fin.seekg(0, std::ios::beg);
//C++11
//The function clears the eofbit flag, if set before the call.
实际写代码时是如下的连续调用:
fin.seekg(offset, std::ios::beg).read((char*)pbsdf->M.data(), number * sizeof(float));
此时在C++17标准下seekg好像并没有清除eofbit,读取依然失败,数据不对。可能不能连续调用,需要分离一步步调用才可以。