你的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 元素数组时,可以使用0 到count-1 访问这些元素。您访问的元素 count 超出范围,因此您的程序具有未定义的行为。
- VLA:s(可变长度数组)在标准 C++ 中不存在(但在某些编译器中作为扩展存在)。如果您确切知道需要存储多少元素,则可以改用
std::array,但在这种情况下,请使用std::vector。
- 它使用全局
file 变量(甚至不存在)。如果可以的话,尽量远离全局变量。
数据文件中的记录应该放在一起,而不是把每一列放在一个单独的数组中。文件中每条记录的简单占位符如下所示:
struct record {
std::string name{};
int vote{};
};
这样,您只需要一个数组(或std::vector)。
std::vector<record> records;
如果可以使用与int 和std::string 相同的>> 运算符从流中提取一个完整的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
}
该函数通过引用获取两个参数(is 和 r)。这意味着对函数内部参数所做的任何事情都会影响用于调用函数的变量。 file >> 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 特性,有助于为变量创建一个狭窄的范围。