了解如何读入文件并将文件中的行分成不同的变量
Understanding How to Read Into A File & Separate Lines from File into Different Variables
所以这是我的困境。我今天要做一项家庭作业,包括从文件中读取数据,将行分隔成各种变量,并根据某些参数显示行。我(以及我的同学)遇到的问题是,我们的教授非常不善于教学,而且我们的投影仪目前坏了,她无法从预先给定的幻灯片中进行教学,而是100%依赖于她提出的例子,很少解释。不仅如此,我已经为此工作了几个小时,现在是凌晨4:30,不管教授是谁,我都不擅长编程。我从来都不是很好,这实际上会让我改变专业,因为我无法掌握窍门。基本上,我只需要知道朝着正确的方向应该采取什么步骤,否则我会彻夜未眠,一天的休息会很糟糕。
我们的任务包括从农场列表中获取数据,该列表还包括多个项目、所述项目的描述、每个项目的价格以及所述项目总成本乘以每个项目的成本,所有这些都在每个"完整"列表的一行上。如果之前在文件中提到了农场本身(副本可以方便地并排放置),则将项目数量和总价添加到一行中。因此,例如,在"Big Top Farm"的3个列表之间,将显示为一行,共包含10625个项目,总成本为5622.30美元。最后,代码旨在打印出特定数量的"唯一"农场(那些有重复条目的农场只包括一次)。我知道我可以用一个简单的计数器整数来处理这个问题,在它读取特定的集合后,用一个快速的++序列,但这是我唯一知道我做得正确的事情。
这是我对代码的绝望尝试(是的,我知道它还没有完成,也没有构建)
#include <fstream>
#include <cstdlib>
#include <string.h>
using std::cin;
using std::cout;
using std::endl;
using std::ifstream;
using std::ofstream;
using std::ios;
using std::string;
//prototypes
void readIn();
int farmDisplay(int, string, double, double);
int main()
{
string farmName, itemType;
int itemCount, farms;
double itemPrice, totalPrice;
cout << "==================================================" << endl;
cout << "= FARMER'S MARKET INVENTORY =" << endl;
cout << "==================================================" << endl;
farms = farmDisplay(itemCount, itemType, itemPrice, totalPrice);
cout << endl;
cout << "There were " << farms << " unique farms contributing to this week's event." << endl;
return 0;
}
//precondition:
//postcondition:
int farmDisplay(int itemCount, string itemType, double itemPrice, double totalPrice)
{
int counter = 0, result, prevItemCount, currentItemCount;
string farmName, prevFarm, currentFarm;
ifstream inFile;
inFile.open("ASSGN6-B.txt");
//Check for Error
if(inFile.fail())
{
cout << "Error opening file..." << endl;
exit(1);
}
while(!inFile.eof())
{
cin.ignore();
getline(inFile, currentFarm, ',');
if(prevFarm.compare(currentFarm) == 0)
{
prevFarm = currentFarm;
prevItemCount == currentItemCount;
counter--;
}
else
{
prevFarm = currentFarm;
prevItemCount == currentItemCount;
}
inFile >> itemCount >> itemType >> itemPrice >> totalPrice;
cout << farmName << " " << itemCount << " items contributed totaling $" << totalPrice << endl;
counter++;
}
inFile.close();
return counter;
}
下面是我们得到的文件的样子:
Collins Farm, 43900 tomatoes 0.67 29413
Bart Smith Farms, 34910 cassavas 0.99 34560.9
Allen Farms, 117 coconuts 0.54 63.18
River Run Farm, 103 taros 0.65 66.95
Big Top Farm, 109 artichokes 2.23 243.07
Big Top Farm, 777 crosns 0.28 217.56
Big Top Farm, 9739 cucumbers 0.53 5161.67
Marble Farm, 108 crosns 0.33 35.64
Food For Life Inc., 106 carrots 0.87 92.22
Food For Life Inc., 86 coconuts 0.84 72.24
Johnson Farms, 121 parsnips 0.22 26.62
A1 Farm, 111 beets 0.12 13.32
A1 Farm, 5591 taros 0.72 4025.52
Looney Tunes Farm, 102 onions 0.49 49.98
Wolfe Creek Farm, 103 rhubarbs 1.21 124.63
Wolfe Creek Farm, 199 radishes 0.71 141.29
James Farm, 47 pickles 0.68 31.96
Weaver Farms, 75 walnuts 2.5 187.5
Weaver Farms, 500 pickles 0.59 295
Pore Boy Farms, 670000 peanuts 0.79 529300
Rutherford Farms Inc., 809 apples 0.9 728.1
Rutherford Farms Inc., 659 pickles 0.7 461.3
Javens Farm, 129000 figs 0.44 56760
Harrison Farms, 8001 yams 1.09 8721.09
Setzer Farms Inc., 701 potatoes 0.89 623.89
Setzer Farms Inc., 651 tomatoes 0.69 449.19
Pikes Peak Farm, 1045 turnips 0.79 825.55
Holland Area Farms, 10001 radishes 0.69 6900.69
任何建议都将不胜感激,因为我觉得我将在这个项目上疯狂工作
好的,我将给你一个通用的方法和一些基本的想法。首先,编码并不容易。这就是为什么我们这些老程序员过着非常好的生活。但你不会只是随便进入其中,它需要奉献精神和好奇心。你必须喜欢它,但考虑一下编程一个巨大的难题。
现在,当你被一项任务压垮时,把任务分解成更小的部分。你真的把它做成了一大块。
这是我要做的。我会创建一个类来表示原始数据。我会创建一个类来加载文件,然后我会编写一个方法来分析数据并将其输出。
在这种情况下,从类开始。
// This holds one line's data
class LineItem {
public:
std::string farmName;
std::string itemName;
int quantitySold;
double priceEach;
double totalPrice;
// You'll need to implement this, see comments below.
LineItem(const std::string fromString);
};
// This holds all the data for a specific farm.
class Farm {
public:
std::string name;
std::vector<LineItem *> lineItems;
};
// And this holds all the farms with the key being the farm name.
std::map<std::string, Farm *> allfarmsByName;
此时,您需要一个方法来读取数据。我会阅读每一行,然后在delimeter(逗号和/或空格)处拆分字符串。第1列为名称,第2列为数量等。
这是一段相对简单的代码,您应该能够编写。所以你可以获取数据行,然后做这样的事情:
LineItem *newItem = new LineItem(myString);
如果你实现了这个构造函数,那么你可以这样做:
Farm * farm = allFarmsByName[newItem->farmName];
if (farm == nullptr) {
farm = new Farm();
farm->name = newItem->farmName;
allFarmsByName.insert(pair<std::string, Farm *>(farm->name, farm));
}
此时,您的allFarmsByName类每个贡献农场有一个项,每个农场都有一个所有数据的向量。
因此,要打印本月帮助了多少农场,您只需要打印allFarmsByName的大小。
现在,我如何做到这一点的细节并不重要。这是方法。把它分解。
- 首先,设想数据并构造类来表示数据
- 其次,将文件中的数据读取到这些对象中
- 然后做你需要做的任何事情来进行分析
这是一种工作模式。远景数据。读取数据。数据报告。
您已经有了一种使用STLstd::vector
将每行的每个组件作为一个单元来实现类的方法,该单元由@JosephLarson提供。通过类实现,您可以提供成员函数来对存储的数据进行操作,以创建农场的抽象
如果这有点超出了你的学习水平,你可以通过保持两套价值观来实现。一个用于正在为其收集值的当前服务器场,另一个用于从文件中读取数据的临时集。任何时候,当您需要将不同类型的数据作为一个单元进行协调时,您都应该考虑struct
或class
(两者在C++中提供相同的功能,不同之处在于默认情况下对struct
成员的访问是public:
,而对class
成员的默认访问是private:
正如我之前的评论中所提到的,如果失败,您当前尝试使用while(!inFile.eof())
读取数据的方法将终止。看看为什么!。循环条件中的eof()总是错误的。为什么iostream::eof在循环条件中被认为是错误的?
相反,对于一般方法,与其尝试直接从文件流中读取对getline
有不同调用的行的部分,不如一次读取一整行,然后从该行创建std::stringstream
,并从字符串流中解析所需的信息。防止部分读取或格式错误从错误点传播到文件末尾。
方法是直接的,从你有开始,但也包括<sstream>
。例如,您可以包含必要的头,并声明一个简单的struct
来容纳每行的不同部分,声明一个结构实例以在代码中使用,验证是否至少有一个参数提供了文件名,将文件名作为程序的第一个参数读取,并打开文件流以使用进行读取
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
struct farmdata {
std::string name, item;
size_t qty;
double cost, total;
};
int main (int argc, char **argv) {
if (argc < 2) {
std::cerr << "error: filename required as 1st argument.n";
return 1;
}
farmdata farm = {"", "", 0, 0.0, 0.0};
size_t farmcount = 0;
std::string line;
std::ifstream f (argv[1]);
...
在这一点上,而不是while(!inFile.eof())
,通过将行实际读取到字符串line
中来控制读取循环,例如
while (getline (f, line)) {
...
现在只需从行中创建一个stringstream
,您可以从中读取name, qty, item, cost & total
,而不会在解析错误或类型不匹配的情况下在流中留下未读的无关字符。这很简单。你还需要声明一个结构的临时实例来读取值,这将允许你将场的name
与你正在收集数据的当前场名称进行比较,例如
std::istringstream ss (line);
farmdata tmp;
现在,只需从字符串流ss
将值读取到临时结构中,就像从文件流读取值一样,然后将值与farm
中的当前值进行比较(请注意,farm
结构已初始化为零,以便测试farm.name.length()
是否为零,指示第一行正在读取到tmp
中):
if (getline (ss, tmp.name, ',')) {
if (ss >> tmp.qty && ss >> tmp.item && ss >>
tmp.cost && ss >> tmp.total) {
if (farm.name.length() == 0) {
farm = tmp;
farmcount++;
}
else if (tmp.name == farm.name) {
farm.qty += tmp.qty;
farm.total += tmp.total;
}
else {
std::cout << farm.name << " " << farm.qty <<
" items contributed totaling $" << farm.total << 'n';
farm = tmp;
farmcount++;
}
}
}
}
(注意:只有当读取到tmp
中的名称与farm.name
不同或在从文件读取的第一行时,才会更新farmcount
)
退出读取循环后,剩下的就是输出从文件中读取的最后一个farm
的数据,并输出本周参与的总farmcount
,
std::cout << farm.name << " " << farm.qty <<
" items contributed totaling $" << farm.total << "nn";
std::cout << "There were " << farmcount <<
" unique farms contributing to this week's event." << 'n';
}
示例使用/输出
如果你实现了类似于上面的东西,你会处理你的文件并收到类似于以下内容的东西:
$ ./bin/farmtotal dat/farms.txt
Collins Farm 43900 items contributed totaling $29413
Bart Smith Farms 34910 items contributed totaling $34560.9
Allen Farms 117 items contributed totaling $63.18
River Run Farm 103 items contributed totaling $66.95
Big Top Farm 10625 items contributed totaling $5622.3
Marble Farm 108 items contributed totaling $35.64
Food For Life Inc. 192 items contributed totaling $164.46
Johnson Farms 121 items contributed totaling $26.62
A1 Farm 5702 items contributed totaling $4038.84
Looney Tunes Farm 102 items contributed totaling $49.98
Wolfe Creek Farm 302 items contributed totaling $265.92
James Farm 47 items contributed totaling $31.96
Weaver Farms 575 items contributed totaling $482.5
Pore Boy Farms 670000 items contributed totaling $529300
Rutherford Farms Inc. 1468 items contributed totaling $1189.4
Javens Farm 129000 items contributed totaling $56760
Harrison Farms 8001 items contributed totaling $8721.09
Setzer Farms Inc. 1352 items contributed totaling $1073.08
Pikes Peak Farm 1045 items contributed totaling $825.55
Holland Area Farms 10001 items contributed totaling $6900.69
There were 20 unique farms contributing to this week's event.
简单地使用"英寸蠕虫"方法检查当前读取的名称与最后读取的名称的缺点是,您没有将数据存储在任何类型的数组或vector
中,这限制了您对完整数据集进行排序或以其他方式操作以获得除"从文件读取"之外的任何其他形式的信息的能力。
您还可以通过包含Standard library header <iomanip>
标头并使用std::setw()
作为常规字段宽度以及std::fixed
和std::count.precision()
作为浮点数格式来进一步定制输出格式。
仔细看看,如果你还有问题,请告诉我。
- 将文件读入数据结构
- 如何检测将文本文件读入 int 数组的新行
- 将整个二进制文件读入缓冲区,然后以特定格式解析
- 如何在C++中将文件的内容读入char16_t数组?
- (C++) 使用诅咒打印从文件中读入的新行
- 使用重定向命令从 stdin 读入的字符串"<"输入文件未正确附加
- 将数据文件读入结构 C++
- 了解如何读入文件并将文件中的行分成不同的变量
- 将文本文件读入类变量 - c++
- C++:读入文件并显示单个项目
- 在 c++ 中将 txt 文件读入多变量维数组
- 按变量数组中的列将一组整数从文本文件读入
- C++将数据文件中的行读入某些变量
- 将一行文件读入对象中的两个变量
- 从文件 c++ 将单个字符读入变量
- 如何将此文件读入变量
- 读入文件C++
- C++ 仅使用数字(双精度)的读入文件
- 从包含多行文本文件中读入浮动变量的代码
- 将不同类型的变量写入二进制文件,然后将其读入类变量,会得到错误的值