一次将文件读入向量
Reading a file into a vector all at once
读入 std::vector
是个好主意,还是这里有一些问题:
using namespace std;
ifstream file("blah.dat", ios::binary);
vector<T> v(N);
file.read(static_cast<char*>(v.data()), N * sizeof(T));
vector
标准是否允许我这样做来填充向量?为了简单起见,让我们假设 T 是一个普通的旧数据类型。
你不应该这样做,事实上你应该做的(从最初的 C++ 开始)是这样
std::ifstream file("foo.txt");
file >> std::noskipws; // use this line so that whitespace won't be skipped
std::vector<char> buffer(std::istream_iterator<char>(file),
std::istream_iterator<char>());
您不应该按照自己的方式进行操作的原因是对象存在于文件中是没有意义的(至少在 C++ 中)。文件只包含字符,然后由 operator>>
格式化以创建对象。该标准允许编译器对一个对象做一些非常奇怪的事情(尤其是当启用 RTTI 时),这使得 "save" 一个对象到一个文件变得毫无用处。你最好为它创建你自己的序列化格式。
如果 T
是平凡可复制的,那么这里没有未定义的行为,而 PODs 肯定是。 vector<T>::data
保证 return 指向 vector<T>::size
T
的连续数组的指针,并且可平凡复制类型 T
的对象表示保证是sizeof(T)
字节的连续序列(可能包括内部填充)。
如果您存储到 space 中的字节不是有效的 T
对象表示,则在访问它们时可能会出现未定义的行为。究竟哪些字节序列构成有效的 T
对象表示是一个灰色区域;至少你应该能够将一个普通可复制类型的对象的底层字节可移植地写入文件,并成功地将它们读回到同一类型对象的底层字节中。
出于偏执狂的缘故,我可能会:
static_assert(std::is_trivially_copyable<T>(),
"NO NO NO - T MUST BE TRIVIALLY COPYABLE!");
在 file.read
之前用于面向未来。
看起来您会受益于内存映射文件。 Boost提供了两种实现方式,不用直接乱用mmap()
#include <boost/iostreams/device/mapped_file.hpp>
boost::iostreams::mapped_file_source file("blah.dat");
std::size_t size = file.size() / sizeof(T);
const T * ptr = reinterpret_cast<const T*>(file.data());
for (std::size_t i=0; i<size; ++i)
std::cout << ptr[i] << std::endl;
您也可以使用 boost.interprocess,但它需要更多代码才能实现基本相同的功能。
主要优点是您无需分配任何内存来访问文件,它将在访问数据时按需加载。数据本身将存在于 cached/buffered 页中,因此它不会带走任何内存(如果系统需要内存用于其他用途,它们将被丢弃。)
读入 std::vector
是个好主意,还是这里有一些问题:
using namespace std;
ifstream file("blah.dat", ios::binary);
vector<T> v(N);
file.read(static_cast<char*>(v.data()), N * sizeof(T));
vector
标准是否允许我这样做来填充向量?为了简单起见,让我们假设 T 是一个普通的旧数据类型。
你不应该这样做,事实上你应该做的(从最初的 C++ 开始)是这样
std::ifstream file("foo.txt");
file >> std::noskipws; // use this line so that whitespace won't be skipped
std::vector<char> buffer(std::istream_iterator<char>(file),
std::istream_iterator<char>());
您不应该按照自己的方式进行操作的原因是对象存在于文件中是没有意义的(至少在 C++ 中)。文件只包含字符,然后由 operator>>
格式化以创建对象。该标准允许编译器对一个对象做一些非常奇怪的事情(尤其是当启用 RTTI 时),这使得 "save" 一个对象到一个文件变得毫无用处。你最好为它创建你自己的序列化格式。
如果 T
是平凡可复制的,那么这里没有未定义的行为,而 PODs 肯定是。 vector<T>::data
保证 return 指向 vector<T>::size
T
的连续数组的指针,并且可平凡复制类型 T
的对象表示保证是sizeof(T)
字节的连续序列(可能包括内部填充)。
如果您存储到 space 中的字节不是有效的 T
对象表示,则在访问它们时可能会出现未定义的行为。究竟哪些字节序列构成有效的 T
对象表示是一个灰色区域;至少你应该能够将一个普通可复制类型的对象的底层字节可移植地写入文件,并成功地将它们读回到同一类型对象的底层字节中。
出于偏执狂的缘故,我可能会:
static_assert(std::is_trivially_copyable<T>(),
"NO NO NO - T MUST BE TRIVIALLY COPYABLE!");
在 file.read
之前用于面向未来。
看起来您会受益于内存映射文件。 Boost提供了两种实现方式,不用直接乱用mmap()
#include <boost/iostreams/device/mapped_file.hpp>
boost::iostreams::mapped_file_source file("blah.dat");
std::size_t size = file.size() / sizeof(T);
const T * ptr = reinterpret_cast<const T*>(file.data());
for (std::size_t i=0; i<size; ++i)
std::cout << ptr[i] << std::endl;
您也可以使用 boost.interprocess,但它需要更多代码才能实现基本相同的功能。
主要优点是您无需分配任何内存来访问文件,它将在访问数据时按需加载。数据本身将存在于 cached/buffered 页中,因此它不会带走任何内存(如果系统需要内存用于其他用途,它们将被丢弃。)