首页 > 解决方案 > 如何编写将字符串和整数读入两个单独的动态数组的程序?

问题描述

我正在为一个类编写一个程序,该类从 .txt 文件中获取输入,并根据变量类型将其放入单独的数组中。

这是输入文件的示例

6 8
Cody Coder  84 100 100 70 100 80 100 65
Harry Hardware  77 68 65 100 96 100 86 100
Harry Potter  100 100 95 91 100 70 71 72
Mad Mulligun  88 96 100 90 93 100 100 100
George Washington  100 72 100 76 82 71 82 98
Abraham Lincoln  93 88 100 100 99 77 76 93

因此,我应该使用第一行来获取姓名的数量(在本例中为 6)和考试分数的数量(8),然后使用它来初始化两个动态数组。我相信我正在正确初始化数组,我只是很难用输入文件中的正确数据填充数组。这个学期之前我从来没有读过文件,所以我完全不熟悉。

#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <string>

using namespace std;

#ifdef _MSC_VER
#define _CRTDBG_MAP_ALLOC  
#include <crtdbg.h>
#define VS_MEM_CHECK _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#else
#define VS_MEM_CHECK
#endif

int main(int argc, char* argv[]) {
    VS_MEM_CHECK
    // Variables
    int numNames = 32;
    int numExams = 32;

    // Part 1: Read scores from the input file
    cout << "Input file: " << argv[1] << endl;
    ifstream in(argv[1]);
    if (!in) {                                              // fail case
        cerr << "Unable to open " << argv[1] << " for input";
        return 2;
    }
    cout << "Output file: " << argv[2] << endl;
    ofstream out(argv[2]);
    if (!out) {                                             // fail case
        in.close();
        cerr << "Unable to open " << argv[2] << " for output";
        return 3;
    }

    if (in.is_open()) {
        in >> numNames >> numExams;
        // dynamic string array
        string* fullNames = new string[numNames];                   // new: initialize names array
        // 2-d, dynamic double array
        double** scores = new double* [numNames];                   // new: initialize rows of scores array
        for (int i = 0; i < numNames; ++i) {
            scores[i] = new double[numExams];                       // new: initialize columns of scores array
        }

        string currLine;

        for (int i = 0; i < numNames; ++i) {
            string firstName, lastName;

            in >> firstName >> lastName;
            for (int j = 0; j < numExams; ++j) {
                in >> scores[i][j];
            }
            fullNames[i] = firstName + " " + lastName;
        }

    }
    else {
        return -2;
    }

    if (out.is_open()) {

    }
    else {
        return -3;
    }
    return 0;
}

我抛出了一个异常(我认为是因为试图访问不存在的东西)并且我需要使用数组。如果我可以使用向量,我就不会遇到问题。

编辑:我被要求显示声明 numNames 和 numExams 的代码,它位于 main 的顶部附近。编辑 2:包括我在下面使用的所有库


谢谢你。

标签: c++arraysvisual-studio

解决方案


基本上你的程序正在运行。我编译了它并修复了一些小问题。

但非常重要的是,你释放你用 new 分配的内存。

请在此处查看固定示例:

#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <string>


int main(int argc, char* argv[]) {
    // Variables
    int numNames = 32;
    int numExams = 32;

    // Part 1: Read scores from the input file
    std::cout << "Input file: " << argv[1] << std::endl;
    std::ifstream in(argv[1]);
    if (!in) {                                              // fail case
        std::cerr << "Unable to open " << argv[1] << " for input";
        return 2;
    }
    std::cout << "Output file: " << argv[2] << std::endl;
    std::ofstream out(argv[2]);
    if (!out) {                                             // fail case
        in.close();
        std::cerr << "Unable to open " << argv[2] << " for output";
        return 3;
    }

    if (in.is_open()) {
        in >> numNames >> numExams;
        // dynamic string array
        std::string* fullNames = new std::string[numNames];                   // new: initialize names array
        // 2-d, dynamic double array
        double** scores = new double* [numNames];                   // new: initialize rows of scores array
        for (int i = 0; i < numNames; ++i) {
            scores[i] = new double[numExams];                       // new: initialize columns of scores array
        }

        std::string currLine;

        for (int i = 0; i < numNames; ++i) {
            std::string firstName, lastName;

            in >> firstName >> lastName;
            for (int j = 0; j < numExams; ++j) {
                in >> scores[i][j];
            }
            fullNames[i] = firstName + " " + lastName;
        }

        if (out.is_open()) {
            for (int i = 0; i < numNames; ++i) {
                out << fullNames[i] << ":";
                for (int j = 0; j < numExams; ++j) {
                    out << " " << scores[i][j];
                }
                out << "\n";
            }
        }

        // Release memory
        for (int i = 0; i < numNames; ++i) {
            delete[] scores[i];
        }
        delete[] scores;
        delete[] fullNames;

        if (!out.is_open()) {
            return -3;
        }
    }
    else {
        return -2;
    }
    return 0;
}

如果我们在软件上投入更多的精力,那么它可能看起来像这样:

#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <string>

// Read file with students and scores and copy write the read data to a new file
int main(int argc, char* argv[]) {

    // Check, if the program has been called with the expected numbers of parameters
    if (3 == argc) {
        // Give feddback to user about filenames:
        std::cout << "\nProgram will work with files: '" << argv[1] << "' and '" << argv[2] << "'\n";

        // Try to open the input file and try to open the output file
        if (std::ifstream in(argv[1]); in) {
            if (std::ofstream out(argv[2]); out) {

                // All files are open. Read number of data. Check, if read worked
                if (size_t numNames{}, numExams{}; (in >> numNames >> numExams) && (numNames > 0U) && (numExams > 0U)) {

                    // We found plausible data Now, allocate memory
                    std::string* fullNames = new std::string[numNames];                   // new: initialize names array
                    // 2-d, dynamic double array
                    double** scores = new double* [numNames];                   // new: initialize rows of scores array
                    for (int i = 0; i < numNames; ++i) {
                        scores[i] = new double[numExams];                       // new: initialize columns of scores array
                    }

                    // Read data
                    for (size_t i = 0U; i < numNames; ++i) {
                        std::string firstName, lastName;
                        in >> firstName >> lastName;
                        for (size_t j = 0U; j < numExams; ++j) {
                            in >> scores[i][j];
                        }
                        fullNames[i] = firstName + " " + lastName;
                    }
                    // Write data
                    for (size_t i = 0U; i < numNames; ++i) {
                        out << fullNames[i] << ":";
                        for (size_t j = 0U; j < numExams; ++j) {
                            out << " " << scores[i][j];
                        }
                        out << "\n";
                    }

                    // Release memory
                    for (int i = 0; i < numNames; ++i) {
                        delete[] scores[i];
                    }
                    delete[] scores;
                    delete[] fullNames;
                }
            }
            else {
                std::cerr << "\nError: Could not open output file '" << argv[2] << "'\n";
            }
        }
        else {
            std::cerr << "\nError: Could not open input file '" << argv[1] << "'\n";
        }
    }
    else {
        std::cerr << "\nError: Please call program with 2 filenames for input data and output data\n\n";
    }
    return 0;
}

最后一步是采用更现代的 C++ 方法。

在 C++ 中,我们不将原始指针用于拥有的内存,也不应使用 new。如果我们应该使用std::unique_ptrand std::make_unique。但是,不适用于数组。

对于动态数组,我们将使用 alwaysstd::vector或类似的。它们几乎完美地满足了所要求的任务。而且,它让生活更轻松。

那么,C++ 是一种面向对象的语言。我们正在使用对象,由数据和对数据进行操作的函数组成。

因此,我定义了一个类(结构)Student,我们将在其中放置名称和分数。由于这是一个对象,它知道如何读取和写入其数据。在课堂之外,没有人关心这个。

我们为这个类覆盖插入器和提取器运算符,然后可以使用与标准数据类型相同的方式与插入器和提取器运算符一起使用类的实例。所以,可以写std::cout << student

这使得进一步的操作更加容易。它确实更适合其他algorithm的 C++。

重要通知。这种方法不需要输入文件的第一行。它是完全动态的!此外,分数的数量可能因行而异。


让我们看看提取器。它首先读取一个完整的行并将这个完整的行放入一个 istringstream 对象中。

接下来我们定义一个std::vector名为“part”的a,并使用它的范围构造函数来填充它。范围构造函数获取某个iterator范围的开头和某个范围的结尾作为参数,并将数据从那里复制到自身中。

开头iteratorstd:istream_iterator,在这种情况下是std::string。这ìterator将简单地调用给定流的提取器运算符 ( >> ),直到读取所有数据。结尾用 {} 标记。这是默认构造的std::istream_iterator,称为流结束迭代器。请看这里

请注意:我们可以在没有模板参数的情况下定义 std::vector。编译器可以从给定的函数参数中推断出参数。此功能称为 CTAD(“类模板参数推导”)。

因此,我们在 中std::vector拥有名称为“part”的所有子字符串。在这个函数的最后,我们简单地将相关部分复制到我们的类内部数据中。

对于分数,我们将字符串转换为双精度,并使用std::back_inserter将分数动态添加到我们的分数中vector


输出,插入操作符 ( << ) 要简单得多。我们只需将结果复制到流中。在这里,我们还利用std::copy了标准算法库和std::ostream:iterator. 这个迭代器也很有帮助。它只是为每个给定元素调用插入器运算符( << )。

因此,在定义了类之后,main 中的完整程序归结为 2 个主要语句:

                // Define Roster and read all students
                std::vector roster(std::istream_iterator<Student>(in), {});

                // Write complete roster to out file
                std::copy(roster.begin(), roster.end(), std::ostream_iterator<Student>(out, "\n"));

使用 2 行代码,我们可以: 1. 读取完整文件, 2 将其写入所需的输出文件。请再次注意,即使这样也可以优化为一个语句:

std::copy(std::istream_iterator<Student>(in), {}, std::ostream_iterator<Student>(out, "\n"));

所以,从本质上讲,我们最终得到了一个单班轮。. .

请查看完整的代码示例:

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

struct Student {
    // Data for one student
    std::string firstName{};
    std::string lastName{};
    std::vector <double>scores{};

    // Specific extractor operator
    friend std::istream& operator >> (std::istream& is, Student& st) {

        // Read one complete line from the source stream
        if (std::string line{}; std::getline(is, line)) {

            // Put the line in a istringstream
            std::istringstream iss(line);

            // Split the line and get all substrings into a vector
            std::vector part(std::istream_iterator<std::string>(iss), {});

            // Copy the string parts to our local data
            if (part.size() > 1) {
                // Copy name
                st.firstName = part[0]; st.lastName = part[1];

                // Copy scores
                st.scores.clear();
                std::transform(std::next(part.begin(), 2), part.end(), std::back_inserter(st.scores), [](const std::string& s) {return std::stod(s); });
            }
        }
        return is;
    }

    friend std::ostream& operator << (std::ostream& os, const Student& st) {
        os << st.firstName << " " << st.lastName << ": ";
        std::copy(st.scores.begin(), st.scores.end(), std::ostream_iterator<double>(os, " "));
        return os;
    }


};

// Read file with students and scores and copy write the read data to a new file
int main(int argc, char* argv[]) {

    // Check, if the program has been called with the expected numbers of parameters
    if (3 == argc) {
        // Try to open the input file and try to open the output file
        if (std::ifstream in(argv[1]); in) {
            if (std::ofstream out(argv[2]); out) {


                // Define Roster and read all students
                //std::vector roster(std::istream_iterator<Student>(in), {});

                // Write complete roster to out file
                //std::copy(roster.begin(), roster.end(), std::ostream_iterator<Student>(out, "\n"));

                // Copy complete input to output.
                std::copy(std::istream_iterator<Student>(in), {}, std::ostream_iterator<Student>(out, "\n"));


            } else {    std::cerr << "\nError: Could not open output file '" << argv[2] << "'\n";}
        } else {    std::cerr << "\nError: Could not open input file '" << argv[1] << "'\n"; }
    } else {    std::cerr << "\nError: Please call program with 2 filenames for input data and output data\n\n";}

    return 0;
}

很遗憾,您很可能会继续使用您的new解决方案。. .


推荐阅读