我有一些代码从.txt文件中获取名称列表和 double 值列表,并将其显示在命令提示符下。为此,将动态分配结构数组。代码应基于.txt文件中的第一个值来了解数组的大小,然后再跟随其后的名称和相关值。然后,它应该分两部分显示列表,其名称的关联 double 值大于或等于首先列出的10.000。如果没有一个值符合要求,则在上半部分显示“无”。
该程序会执行,但是调试器会给出异常,并且输出与预期不符。
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
struct donor
{
string name;
double contribution = 0;
};
int main()
{
string filename;
ifstream inFile;
cout << "Enter name of data file: ";
cin >> filename;
inFile.open(filename);
cin.clear();
if(!inFile.is_open())
{
cout << "Could not open the file " << filename << endl;
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount;
cin.clear();
donor* dlist = new donor[amount];
int i;
while(inFile.good())
{
for(i = 0; i < amount; i++)
{
getline(inFile, dlist[i].name);
cin.clear();
inFile >> dlist[i].contribution;
cin.clear();
}
}
cout << "Here's the list of Grand Patrons:\n";
bool grandpatrons = false;
for(i = 0; i < amount; i++)
{
if(dlist[i].contribution >= 10000)
{
grandpatrons = true;
cout << dlist[i].name << endl;
cout << dlist[i].contribution << endl;
}
}
if(grandpatrons == false)
{
cout << "None" << endl;
}
cout << "Here's the list of Patrons:\n";
for (i = 0; 1 < amount; i++)
{
if (dlist[i].contribution < 10000)
{
cout << dlist[i].name << endl;
cout << dlist[i].contribution << endl;
}
}
delete[] dlist;
return 0;
}
施主列表.txt文件如下所示:
4
Bob
400
Alice
11000
但是输出看起来像这样:
Enter name of data file: donorlist.txt
Here's the list of Grand Patrons:
None
Here's the list of Patrons:
0
0
0
0
调试器给我的异常(exception)是:
Exception thrown at 0x5914F3BE (ucrtbased.dll) in 6_9.exe: 0xC0000005: Access violation reading location 0xA519E363.
现在,我假设从动态分配的内存中读取数据出了问题。也许是某种原因导致我从分配的数组之外的内存中读取数据?我无法准确找到错误所在。
最佳答案
您的问题始于在数据文件中写入错误的amount
。
使用以下方法修复:
2
Bob
400
Alice
11000
然后,他们继续错误地读取文件。
请记住:混合
operator>>
和getline()
并不像看起来那样简单。您会看到
operator>>
IGNORES newline
和space
字符,直到找到其他任何字符为止。然后,它将读取即将到来的字符,直到遇到下一个
newline
或space
字符,但不要丢弃它。这是
getline
出现问题的地方。getline
读取所有内容,直到遇到newline
或指定的delim
字符。这意味着,如果您的
operator>>
在遇到newline
后停止,则getline
将立即读取newline
,从而显示NOTHING。要解决此问题,您需要处理
newline
字符。为此,您可以先检查流中的下一个字符是否确实是
newline
,然后在其上使用istream::ignore()
;int next_char = stream.peek();
if(next_char == '\n'){
stream.ignore();
}
您的代码的有效示例为:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//Suggestion: class/struct names should start with a capital letter.
struct Donor{
//Suggestion: Use member initializer lists to specify default values.
Donor() : name(), contribution(0){}
string name;
double contribution;
};
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
//Suggestion: Open the file immediately with the filename and use `operator bool` to check if it opened.
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount; //! Leaves '\n'
Donor* donors = new Donor[amount];
for(int i = 0; i < amount; ++i){
switch(inFile.peek()){
case '\n': inFile.ignore();
break;
case EOF: cout << "Donor amount too big!\n";
exit(EXIT_FAILURE);
}
getline(inFile, donors[i].name);
inFile >> donors[i].contribution;
}
cout << "Here's the list of Grand Patrons:\n";
bool grandpatrons_exist = false;
for(int i = 0; i < amount; ++i){
if(donors[i].contribution >= 10000){
grandpatrons_exist = true;
cout << donors[i].name << '\n';
cout << donors[i].contribution << '\n';
}
}
if(!grandpatrons_exist){
cout << "None\n";
}
cout << "Here's the list of Patrons:\n";
for(int i = 0; 1 < amount; ++i){
if(donors[i].contribution < 10000){
cout << donors[i].name << '\n';
cout << donors[i].contribution << '\n';
}
}
delete[] donors;
return 0;
}
现在,一个更好的解决方案是使用 vector 而不是原始指针并实现
operator>>
和operator<<
,这将大大简化读取和打印对象。
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class Donor{
public:
Donor() noexcept: name(), contribution(0){}
friend istream& operator>>(istream& stream, Donor& donor){
switch(stream.peek()){
case EOF: return stream;
case '\n': stream.ignore();
}
getline(stream, donor.name);
stream >> donor.contribution;
return stream;
}
friend ostream& operator<<(ostream& stream, const Donor& donor){
stream << donor.name << ' ' << donor.contribution;
return stream;
}
const string& get_name() const noexcept{
return name;
}
const double& get_contribution() const noexcept{
return contribution;
}
private:
string name;
double contribution;
};
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount;
vector<Donor> donors(amount);
//Read it as `for donor in donors`
for(Donor& donor : donors){
inFile >> donor;
}
//An STL function that takes a lambda as the thirs argument. You should read up on them if you haven't.
//I would prefer using this since it greatly improves readability.
//This isn't mandatory, your implementation of this part is good enough.
bool grandpatrons_exist = any_of(begin(donors), end(donors), [](const Donor& donor){ return donor.get_contribution() >= 10000; });
cout << "Here's the list of Grand Patrons:\n";
if(grandpatrons_exist){
for(const Donor& donor : donors){
if(donor.get_contribution() >= 10000){
cout << donor << '\n';
}
}
}
else{
cout << "None\n";
}
cout << "\nHere's the list of Patrons:\n";
for(const Donor& donor : donors){
if(donor.get_contribution() < 10000){
cout << donor << '\n';
}
}
return 0;
}
其他一些重大改进将是:
partition
将出色的顾客与正常顾客分开。 int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
//Ignore the first line completely
inFile.ignore(numeric_limits<streamsize>::max(), '\n');
//Calls `operator>>` internally
vector<Donor> donors(istream_iterator<Donor>{inFile}, istream_iterator<Donor>{});
auto first_grand_patron = partition(begin(donors), end(donors), [](const Donor& donor){ return donor.get_contribution() >= 10000; });
cout << "Here's the list of Grand Patrons:\n";
if(first_grand_patron == begin(donors)){
cout << "None!\n";
}
for(auto patron = begin(donors); patron != first_grand_patron; ++patron){
cout << *patron << '\n';
}
cout << "\nHere's the list of Patrons:\n";
for(auto patron = first_grand_patron; patron != end(donors); ++patron){
cout << *patron << '\n';
}
return 0;
}
现在一些一般性提示:
cin.clear()
。 Cin仅使用一次,不再使用。 ++i
循环中使用i++
代替for
来习惯于递增变量的正确方法,除非另有需要。 bool grandpatrons
太多是一个标志的抽象名称。 donors
是主观上更好的名称,而不是捐助者列表的简称。 关于c++ - 在0x5914F3BE(ucrtbased.dll)引发异常,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57988946/