了解如何读入文件并将文件中的行分成不同的变量

Understanding How to Read Into A File & Separate Lines from File into Different Variables

本文关键字:变量 读入文件 文件 了解      更新时间:2023-10-16

所以这是我的困境。我今天要做一项家庭作业,包括从文件中读取数据,将行分隔成各种变量,并根据某些参数显示行。我(以及我的同学)遇到的问题是,我们的教授非常不善于教学,而且我们的投影仪目前坏了,她无法从预先给定的幻灯片中进行教学,而是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的大小。

现在,我如何做到这一点的细节并不重要。这是方法。把它分解。

  1. 首先,设想数据并构造类来表示数据
  2. 其次,将文件中的数据读取到这些对象中
  3. 然后做你需要做的任何事情来进行分析

这是一种工作模式。远景数据。读取数据。数据报告。

您已经有了一种使用STLstd::vector将每行的每个组件作为一个单元来实现类的方法,该单元由@JosephLarson提供。通过类实现,您可以提供成员函数来对存储的数据进行操作,以创建农场的抽象

如果这有点超出了你的学习水平,你可以通过保持两套价值观来实现。一个用于正在为其收集值的当前服务器场,另一个用于从文件中读取数据的临时集。任何时候,当您需要将不同类型的数据作为一个单元进行协调时,您都应该考虑structclass(两者在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::fixedstd::count.precision()作为浮点数格式来进一步定制输出格式。

仔细看看,如果你还有问题,请告诉我。