嗯,我正在为大学编写一个程序,我必须将数据转储转换为HDF格式。数据转储如下所示:
"1444394028","1","5339","M","873"
"1444394028","1","7045","V","0.34902"
"1444394028","1","7042","M","2"
"1444394028","1","7077","V","0.0470588"
"1444394028","1","5415","M","40"
"1444394028","1","7077","V","0.462745"
"1444394028","1","7076","B","10001101"
"1444394028","1","7074","M","19"
"1444394028","1","7142","M","16"
"1444394028","1","7141","V","0.866667"
对于HDF5 API,我需要一个数组。所以我现在的方法是,将数据转储写入这样的数组中:
int count = 0;
std::ifstream countInput("share/ObservationDump.txt");
std::string line;
if(!countInput) cout << "Datei nicht gefunden" << endl;
while( std::getline( countInput, line ) ) {
count++;
}
cout << count << endl;
struct_t writedata[count];
int i = 0;
std::ifstream dataInput("share/ObservationDump.txt");
std::string line2;
char delimeter(',');
std::string timestampTemp, millisecondsSinceStartTemp, deviceTemp, typeTemp, valueTemp;
while (std::getline(dataInput, timestampTemp, delimeter) )
{
std::getline(dataInput, millisecondsSinceStartTemp, delimeter);
std::getline(dataInput, deviceTemp, delimeter);
std::getline(dataInput, typeTemp, delimeter);
std::getline(dataInput, valueTemp);
writedata[i].timestamp = atoi(timestampTemp.substr(1, timestampTemp.size()-2).c_str());
writedata[i].millisecondsSinceStart = atoi(millisecondsSinceStartTemp.substr(1, millisecondsSinceStartTemp.size()-2).c_str());
writedata[i].device = atoi(deviceTemp.substr(1, deviceTemp.size()-2).c_str());
writedata[i].value = atof(valueTemp.substr(1, valueTemp.size()-2).c_str());
writedata[i].type = *(typeTemp.substr(1, typeTemp.size()-2).c_str());
i++;
}
struct_t
定义为
struct struct_t
{
int timestamp;
int millisecondsSinceStart;
int device;
char type;
double value;
};
正如你们中的一些人可能看到的那样,对于大数据转储(大约 60,000 行),数组writedata
往往会产生堆栈溢出(分段错误)。我需要一个数组来传递它到我的 HDF 适配器。如何防止溢出?我无法通过广泛的谷歌搜索找到答案。提前感谢!
您遵循的示例代码是 C 语言,而您正在编写的代码是 C++。在大多数情况下,有效的 C 代码C++代码是有效的,尽管不一定是好的样式;这是它不是的时候之一,尽管由于这不是你真正的问题,我将在我的答案末尾留下解释。
当您声明struct_t writedata[count];
时,您将在堆栈上创建一个数组。堆栈的大小通常被人为限制,因此在堆栈上创建大型数组可能会导致堆栈空间不足的问题。这就是你所看到的。典型的解决方案是在堆中创建大型数据结构(尽管堆的主要用途是使数据持续超过创建它的函数的return
)。
访问堆的最C++惯用方法是不要直接执行此操作,而是使用帮助程序容器类。在这种情况下,您想要的是std::vector
,它允许您将数据推送到末尾,并且会随着您推送更多数据而自动增长。由于它会自动增长,因此您无需提前指定大小;只需将其声明为std::vector<struct_t> writedata;
(阅读"std::vector
ofstruct_t
")。同样,由于它不需要提前知道大小,因此您也可以忽略整个第一个循环。
向量最初是空的;要将数据放入其中,通常需要使用writedata.push_back()
或writedata.emplace_back()
。其中第一个采用现有的struct_t
;第二个采用用于创建参数的参数。所有元素都连续存储在内存中,就像在 C 数组中一样,您可以使用writedata.data()
直接访问该数组。
在函数结束时,当vector
超出范围且不再可访问时,将调用其析构函数并自动清理它使用的内存。
另一种选择,而不是使用std::vector
,是自己管理内存。这样做C++方法是new
和delete
.处理这个问题的最简单方法是仍然像您一样计算count
,但不是通过仅将其声明为count
大小的数组来在堆栈上创建数组,而是struct_t* writedata = new struct_t[count];
。这将在堆中创建count
struct_t
的数组,并将writedata
设置为指向此数组的第一个元素的指针。然后,您可以像在程序中使用数组一样使用它,但由于它在堆上,因此不会耗尽堆栈空间。
这样做的缺点是您需要提前知道大小,并且需要清理自己使用的内存。为此,当您不再需要数据时,应运行delete[] writedata
。之后,writedata
仍将指向内存中的同一位置,但您的程序不再拥有该数据,因此您需要确保永远不再使用该值;标准方法是在删除后立即将writedata
设置为nullptr
。
您还可以使用 C 等价物来new
和delete
,它们是malloc
和free
。在您的情况下,它们大多是等效的,但对于更复杂的示例,您应该记住,这些使内存未初始化,而new
和delete
将运行您创建的构造函数/析构函数,以确保对象在开始时处于理智状态,并且不会在结束时留下资源。
现在,对于为什么您的原始代码实际上对任何大小的文件都无效C++:您的行struct_t writedata[count];
尝试创建一个count
struct_t
数组。由于count
是一个变量,因此称为可变长度数组(VLA)。这些事情在较新版本的 C 中是合法的,但在C++中不是。只要您只想在当前使用的同一系统上编译代码,仅此一项就值得警告,因为您的编译器似乎支持 VLA 作为扩展。但是,如果要在任何其他系统上编译代码(使其更具可移植性),则不应使用这样的编译器扩展。
struct_t writedata[count];
这个数组被分配到通常非常小的堆栈上,当它达到一个太大的值(这是半任意的)时,这将溢出堆栈。 您最好通过执行以下操作来分配堆:
struct_t* writedata = (struct_t*)malloc(sizeof(struct_t) * count);
然后在完成内存后添加相应的调用以释放,例如
free(writedata);
writedata = nullptr;
最佳做法是检查 while 循环中的i < count
,就好像您注销数组的末尾一样 可能会发生坏事。