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

时间:2019-06-12 21:34:48

标签: c++

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

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

我已经尝试过此代码,但是它如我所愿地崩溃了。

2 个答案:

答案 0 :(得分:0)

我不完全了解您想做什么。缺少信息。无论如何。我会尽力的。

我想您是C ++的新手。您正在使用许多C函数。您的程序看起来完全像C,带有一些其他C ++功能。那你不应该做。您尤其在使用malloc和raw指针。这是您完全不能做的。

尝试逐步学习C ++。

首先让我向您展示C语言风格编程的含义。我接受了您的程序,并添加了带有提示的注释。


// 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 file\n";
    }
    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个元素(单词)的向量。这与源文件中每行少于或少于ncols个单词无关。

希望我至少可以帮上忙。

答案 1 :(得分:0)

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];

您无需检查读取是否成功。那从来都不是一件好事,并且使您的程序无法调试。

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