从文件行读取未定义数量的变量

Read undefined number of variables from file line

本文关键字:变量 未定义 文件 读取      更新时间:2023-10-16

我正在用C++制作一个简单的数据库系统。 表数据存储在文件中,其中每行表示一个表行,其中所有数据都用空格分隔。 我想在同一行中读取 ncols 元素,其中 ncols 并不总是相同的,并将每个读取值存储在 data[x] 中。 数据变量声明是字符**数据。

void Table::LoadTableRows(Table::TableStruct *table,char *dbname) {
ifstream fp;
Table::RowStruct *p = (Table::RowStruct*) malloc(sizeof(Table::RowStruct));
char *filename;
int x;
filename = (char*) malloc((strlen(table->tablename)+strlen(dbname)+strlen("Data"))*sizeof(char));
strcpy(filename,dbname);
strcat(filename,table->tablename);
strcat(filename,"Data");
fp.open(filename);
while(!fp.eof()) { //goes through all file lines
Table::RowStruct *newrow = (Table::RowStruct*) malloc(sizeof(Table::RowStruct)); //allocates space for a new row
//initializes element
newrow->prev = NULL;
newrow->next = NULL;
newrow->data = (char**) malloc(table->ncols*30*sizeof(char)); //allocates space to store the row data
for(x=0;x<table->ncols;x++) {
newrow->data[x] = (char*) malloc(30*sizeof(char)); //allocates space for individual data element
fp >> newrow->data[x];
}
for(p=table->rows;p->next!=NULL;p=p->next) {}
newrow->prev = p;
p->next = newrow;
}
fp.close();
}

我试过这段代码,但它如我预期的那样崩溃了。

我不完全明白你想做什么。缺少信息。无论如何。我会尽力帮忙。

我猜你是C++新手。你正在使用很多 C 函数。你的程序看起来完全像C,有一些额外的C++功能。你不应该这样做。您特别使用 malloc 和原始指针。这你绝对不能这样做。

尝试一步一步地学习C++。

让我首先告诉你我对C-Style编程的含义。我采用了您的程序并添加了带有提示的评论。


// Do not pass arguments by pointer, pass by reference
// For invariants, pass as const T&
// Do not use "char *". Should be at least const. But do not use at all
// Use std::string (so pass "const std::string& dbname") as argument
void Table::LoadTableRows(Table::TableStruct *table,char *dbname) {
// Always initialize varibles. Use universal initialization, with {}
ifstream fp;
// Never use malloc. Use new.
// Do not use raw ptr. use std::unique_ptr. Initialize with std::make_unique
// Do not use C-Style cast. Use static_cast
Table::RowStruct *p = (Table::RowStruct*) malloc(sizeof(Table::RowStruct));
// Use std::string
char *filename;
int x;
// Again. No malloc, no C-Style cast
// Do not use C-Sytle string functions
filename = (char*) malloc((strlen(table->tablename)+strlen(dbname)+strlen("Data"))*sizeof(char));
// Do not use C-Sytle string functions
strcpy(filename,dbname);
// Do not use C-Sytle string functions
strcat(filename,table->tablename);
// Do not use C-Sytle string functions
strcat(filename,"Data");
// Check, if open works, Open file through constructor, then it will be closed by destructor
fp.open(filename);
while(!fp.eof()) { //goes through all file lines
// Do not use malloc and C-Style cast
Table::RowStruct *newrow = (Table::RowStruct*) malloc(sizeof(Table::RowStruct)); //allocates space for a new row
//initializes element
// Do not use NULL, but nullptr
newrow->prev = NULL;
newrow->next = NULL;
// Do not use malloc and C-Style cast
newrow->data = (char**) malloc(table->ncols*30*sizeof(char)); //allocates space to store the row data
// Do not use x++ but ++x
for(x=0;x<table->ncols;x++) {
// Do not use malloc and C-Style cast
newrow->data[x] = (char*) malloc(30*sizeof(char)); //allocates space for individual data element
// Check for out of bounds
fp >> newrow->data[x];
}
// Do not use selfmade linked list. Use STL container
for(p=table->rows;p->next!=NULL;p=p->next) {}
newrow->prev = p;
p->next = newrow;
}
fp.close();
}

你看,里面有很多C,没有那么多C++。

现代C++大量使用容器和算法。

下面是一个完整的C++示例。初学者很难理解。但是尝试分析,你会掌握它的窍门。

#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <sstream>

using AllWordsInOneLine = std::vector<std::string>;
using AllLines =std::vector<AllWordsInOneLine>;
struct Line      // ! This is a proxy for the input_iterator ! 
{   // Input function. Read on line of text file and split it in words
friend std::istream& operator>>(std::istream& is, Line& line) {
std::string wholeLine;  std::getline(is, wholeLine); std::istringstream iss{ wholeLine }; line.allWordsInOneLine.clear(); 
std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), std::back_inserter(line.allWordsInOneLine));
return is;
}
operator AllWordsInOneLine() const { return allWordsInOneLine; }  // cast to needed result
AllWordsInOneLine allWordsInOneLine{};  // Local storage for all words in line
};

int main()
{
std::ifstream inFileStream{ "r:\input.txt" };      // Open input file. Will be closed by destructor
if (!inFileStream) { // ! operator is overloaded
std::cerr << "Could not open input filen";
}
else {
// Read complete input file into memory and organize it in words by lines
AllLines allLines{ std::istream_iterator<Line>(inFileStream), std::istream_iterator<Line>() };
// Make exact ncols entries. 
const size_t ncols = 6; // whatever ncols may be. Empty cols will be filled with ___  (or whatever you like)
std::for_each(allLines.begin(), allLines.end(), [ncols](AllWordsInOneLine& awil) {awil.resize(ncols, "___"); });
// copy result to std::cout
std::for_each(allLines.begin(), allLines.end(), [](AllWordsInOneLine & awil) {std::copy(awil.begin(), awil.end(), std::ostream_iterator<std::string>(std::cout, " ")); std::cout << 'n'; });
}
return 0;
}

请特别看到,整个文件,所有行都分成单词,将在函数main的一行代码中读取。

一个额外的单行将其转换为具有确切 ncols 元素(单词)的向量。无论源文件中每行的单词数是多还是少,这都是如此。

希望我至少能帮上一点忙。

char *filename;
filename = (char*) malloc((strlen(table->tablename)+strlen(dbname)+strlen("Data"))*sizeof(char));
strcpy(filename,dbname);
strcat(filename,table->tablename);
strcat(filename,"Data");

这是你的第一个问题。尚未为字符串末尾的终止 NUL 字节分配空间。我不确定为什么您使用 C 样式字符串而不是std::string,但 C 样式字符串在末尾使用零字节来标记字符串的末尾。

fp.open(filename);
while(!fp.eof()) { //goes through all file lines

您滥用eof.它无法预测未来的读取会成功,它不是一个预测未来的函数,而是一个过去的报告函数。

newrow->data = (char**) malloc(table->ncols*30*sizeof(char)); //allocates space to store the row data

这令人费解。类型为char **,这意味着您正在分配指向指针的指针。但是,您为 30 个字符分配了空间。为什么要为指针分配 30 个字符?

fp >> newrow->data[x];

您不检查此读取是否成功。这从来都不是一件好事,会使您的程序无法调试。

这些是立即突出的主要问题。