【问题标题】:Using a function to open a file and then having other functions utilize that file afterwards?使用函数打开文件,然后让其他函数使用该文件?
【发布时间】:2020-03-19 15:04:34
【问题描述】:

我有一个计算机科学任务,要求我有一个单独的函数来打开文件,然后是另一个函数,然后处理该文件中的数据,然后其他一些函数对该数据进行一些操作。无论如何,我在如何让其他功能使用该打开的文件方面遇到了麻烦。带有 '&' 或 '*' 的引用让我感到困惑,我不确定我是否必须使用一个,当然,尽管我很确定我至少必须将一些东西传递给下一个函数。处理文件时的主要意图是打开它(openFile),然后使用另一个函数(getData)将数据排序到两个不同的数组中。一个用于名称,一个用于旁边的金额。该文件将被写为:

Johnson 6000
Brown 5000
Miller 4000
Duffy 2500
Robson 1800

我的代码如下:

'''

#include <iostream>
#include <string>
#include <iomanip>
#include <fstream>
using namespace std;

void openFile();
void getData();
void computePercentages();
void sortVotes();
void display();
void displayWinner();

int main() {
    openFile();
    getData();

    return 0;
}

void openFile(){
    string fileName;
    cout << "Enter the name of the file to open: ";
    cin >> fileName;
    ifstream file;
    file.open(fileName.c_str());
}

void getData(){
    int count = 0;
    while(!file.eof()){
        string names[count];
        int votes[count];
        cin >> names[count];
        cin >> votes[count];
        count ++;
    }
}

'''

【问题讨论】:

  • 提示:你可以让函数返回一个ifstream
  • 顺便说一句,that loop is wrong - 现在改掉那个习惯!是的,我意识到你的老师有 90% 的机会教给你。不,讽刺的是我并没有忘记

标签: c++ arrays function file


【解决方案1】:

一种方法是让openFile 返回文件流对象,然后将其传递给getData

ifstream openFile()
{
    string fileName;
    cout << "Enter the name of the file to open: ";
    cin >> fileName;

    ifstream file(fileName);

    return file;
}

void getData(ifstream &file)
{
    int count = 0;
    while(file){
        string names[count];
        int votes[count];
        cin >> names[count];
        cin >> votes[count];
        count ++;
    }
}

int main()
{
    ifstream file = openFile();
    if (file)
    {
        getData(file);
    }
}

请注意,此答案不会解决您代码中的其他问题。例如,在getData 中,您使用的可变长度数组是非标准的,并且不适用于所有编译器,并且每次都通过while 循环构造和销毁这些数组。

【讨论】:

    【解决方案2】:

    有很多方法可以做到这一点..

    这是一个简单的方法..使用全局变量。

    我将ifstream file; 设为全局..

    这不是好方法..但很简单..

    #include <iostream>
    #include <string>
    #include <iomanip>
    #include <fstream>
    using namespace std;
    
    void openFile();
    void getData();
    void computePercentages();
    void sortVotes();
    void display();
    void displayWinner();
    
    ifstream file;
    
    int main() {
        openFile();
        getData();
    
        return 0;
    }
    
    void openFile(){
        string fileName;
        cout << "Enter the name of the file to open: ";
        cin >> fileName;
        file.open(fileName.c_str());
    }
    
    void getData(){
        int count = 0;
        while(!file.eof()){
            string names[count];
            int votes[count];
            cin >> names[count];
            cin >> votes[count];
            count ++;
        }
    }
    

    【讨论】:

    • using namespace std; 并且文件未关闭。许多功能原型化但从未实现。不是minimal reproducible example 的问题没有类似问题的答案
    【解决方案3】:

    你的getData()函数有问题:

    void getData(){
        int count = 0;
        while(!file.eof()){       // this is almost never the correct check 
            string names[count];  // you declare new VLA:s (non-standard) every iteration
            int votes[count];     // -"-
            cin >> names[count];  // and you put a value in it out of bounds.
            cin >> votes[count];  // -"-
            count ++;
        }                         // both arrays are destroyed here
    }
    
    • file.eof() 不会返回 true,直到您尝试读取文件末尾之外的内容。如果您已读取最后一个值,则不会设置它。只有下次尝试时才会设置。
    • 您在while 循环中声明的数组将在循环结束时被销毁。循环结束后,你就没有数组了。
    • 声明count 元素数组时,可以使用0count-1 访问这些元素。您访问的元素 count 超出范围,因此您的程序具有未定义的行为。
    • VLA:s(可变长度数组)在标准 C++ 中不存在(但在某些编译器中作为扩展存在)。如果您确切知道需要存储多少元素,则可以改用std::array,但在这种情况下,请使用std::vector
    • 它使用全局file 变量(甚至不存在)。如果可以的话,尽量远离全局变量。

    数据文件中的记录应该放在一起,而不是把每一列放在一个单独的数组中。文件中每条记录的简单占位符如下所示:

    struct record {
        std::string name{};
        int vote{};
    };
    

    这样,您只需要一个数组(或std::vector)。

    std::vector<record> records;
    

    如果可以使用与intstd::string 相同的&gt;&gt; 运算符从流中提取一个完整的record,那也很好。像这样:

    record temp;                // declare a variable using your own type, "record"
    while(file >> temp) {       // read records until no more can be read
        records.push_back(temp) // add one record to records
    }
    

    istream 中读取一条记录的函数,例如ifstream

    std::istream& operator>>(std::istream& is, record& r) {
        // You may want to use getline here instead in case the names contain spaces.
        return is >> r.name >> r.vote; // extract name and vote from is and return is
    }
    

    该函数通过引用获取两个参数(isr)。这意味着对函数内部参数所做的任何事情都会影响用于调用函数的变量。 file &gt;&gt; temp 导致调用上述函数,其中is 是对file 的引用,r 是对temp 的引用。

    对于openFile(),我建议:

    std::ifstream openFile(const std::string& fileName) { // return the ifstream by value
        return std::ifstream{fileName};
    }
    

    从用户那里获取文件名与打开文件没有任何关系,所以在调用函数之前获取文件名。上面的函数让你调用openFile() 并得到一个ifstream 作为回报:

    std::ifstream file = openFile(fileName);
    

    您现在可以使用file 呼叫getData(),但它需要能够接收它。标准流对象不能被复制(按值传递),但我们不需要。只需让getData() 接收对流的引用。我将其设为istream 而不是ifstream 以便能够从任何istream 后代中读取:

    std::vector<record> getData(std::istream& is) {
        // create a vector, read data from "is" and put it in vector and return vector when done
    }
    

    当所有的东西拼凑在一起时,你可能会得到一个main(),看起来像这样:

    int main() {
        std::vector<record> records;
    
        std::cout << "Enter the name of the file to open: ";
    
        // use getline since a filename may contain spaces
        if(std::string fileName; std::getline(std::cin, fileName)) {
    
            // if "file" is in a good state after openFile(), call getData()
            if(std::ifstream file = openFile(fileName)) {
    
                records = getData(file);
    
            } // "file" is automatically closed when it goes out of scope
        }
    
        // print what you collected
        for(const record& r : records) {
            std::cout << r.name << "\t" << r.vote << "\n";
        }
    }
    

    上面使用了If Statements with Initializer,这是一个 C++17 特性,有助于为变量创建一个狭窄的范围。

    【讨论】:

      猜你喜欢
      • 2022-06-18
      • 1970-01-01
      • 2010-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多