[英]Read word by word from a file
我有一个 class 是 Food 和 its.h 是:
class Food{
private:
string name;
int protein;
int calories;
int fats;
string type;
public:
....
friend istream & operator >> (istream &i, Food & other);
};
因此,使用运算符 >> 我想从具有以下格式的文件中读取一种食物的属性:
Ketchup;98;24;2;Processed
我试着做:
istream & operator >> (istream &i, Food & other){
string nom, t;
int c, p, h, g, f;
char aux; //Aux stores ';'
i >> nom >> aux >> c >> aux >> p >> aux >> f;
getline(i,t); //I read the type and change the line that I'm reading
Food ing(nom.c_str(),c,p,f,t.c_str()); //Constructor
other = ing; //Overload of operator =
return i;
}
但我没有成功,请帮助?
似乎您想阅读 csv 数据。 这是一个标准任务,我会给你详细的解释。 最后,所有的阅读都将在一行中完成。
我建议使用“现代” C++ 方法。
仍然所有谈论 csv 的人都在链接到How can I read and parse CSV files in C++? ,问题是从 2009 年开始,现在已经 10 多年了。 大多数答案也很旧且非常复杂。 所以,也许是时候改变了。
在现代 C++ 中,您拥有迭代范围的算法。 你会经常看到类似“someAlgoritm(container.begin(), container.end(), someLambda)”的东西。 这个想法是我们迭代一些相似的元素。
在您的情况下,我们遍历输入字符串中的标记,并创建子字符串。 这称为标记化。
正是为了这个目的,我们有std::sregex_token_iterator
。 而且因为我们有为此目的而定义的东西,我们应该使用它。
这东西是一个迭代器。 用于迭代字符串,因此是正则表达式。 开始部分定义了我们将操作的输入范围,然后有一个std::regex
用于在输入字符串中应该匹配/不应该匹配的内容。 匹配策略的类型由最后一个参数给出。
所以,既然我们理解了迭代器,我们就可以将迭代器中的标记 std::copy 复制到我们的目标,即std::string
的std::vector
。 由于我们不知道我们有多少列,我们将使用std::back_inserter
作为目标。 这会将我们从std::sregex_token_iterator
和 append 获得的所有令牌添加到我们的std::vector<std::string>>
中。 我们有多少列并不重要。
好的。 这样的声明可能看起来像
std::copy( // We want to copy something
std::sregex_token_iterator // The iterator begin, the sregex_token_iterator. Give back first token
(
line.begin(), // Evaluate the input string from the beginning
line.end(), // to the end
re, // Add match a semicolon
-1 // But give me back not the comma but everything else
),
std::sregex_token_iterator(), // iterator end for sregex_token_iterator, last token + 1
std::back_inserter(cp.columns) // Append everything to the target container
);
现在我们可以理解,这个复制操作是如何工作的。
下一步。 我们想从文件中读取。 该文件还包含某种相同的数据。 相同的数据是行。
如上所述,我们可以迭代相似的数据。 如果是文件输入或其他。 为此, C++ 具有std::istream_iterator
。 这是一个模板,作为模板参数,它获取应读取的数据类型,作为构造函数参数,它获取对输入 stream 的引用。 没关系,如果输入 stream 是std::cin
或std::ifstream
或std::istringstream
。 所有类型的流的行为都是相同的。
由于我们没有 SO 文件,因此我使用(在下面的示例中) std::istringstream
来存储输入 csv 文件。 当然,您可以通过定义std::ifstream testCsv(filename)
打开文件。 没问题。
使用std::istream_iterator
,我们遍历输入并读取相似的数据。 在我们的例子中,一个问题是我们想要迭代特殊数据而不是一些内置数据类型。
为了解决这个问题,我们定义了一个代理 class,它为我们做内部工作(我们不想知道如何,应该封装在代理中)。 在代理中,我们覆盖了类型转换运算符,以将结果变为我们预期的std::istream_iterator
类型。
最后一个重要的步骤。 std::vector
有一个范围构造函数。 它还有很多其他构造函数,我们可以在std::vector
类型的变量的定义中使用它们。 但是对于我们的目的,这个构造函数最适合。
所以我们定义了一个变量 csv 并使用它的范围构造函数并给它一个范围的开始和一个范围的结束。 而且,在我们的具体示例中,我们使用std::istream_iterator
的开始和结束迭代器。
如果我们结合以上所有内容,阅读完整的 CSV 文件是单行的,它是调用其构造函数的变量的定义。
请查看生成的代码:
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <regex>
#include <algorithm>
std::istringstream testCsv{ R"(Ketchup;98;24;2;Processed
Fries;10;20;2;Processed
Meat;10;20;2;Processed)" };
// Define Alias for easier Reading
using Columns = std::vector<std::string>;
using CSV = std::vector<Columns>;
// Proxy for the input Iterator
struct ColumnProxy {
// Overload extractor. Read a complete line
friend std::istream& operator>>(std::istream& is, ColumnProxy& cp) {
// Read a line
std::string line; cp.columns.clear();
std::getline(is, line);
// The delimiter
const std::regex re(";");
// Split values and copy into resulting vector
std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1),
std::sregex_token_iterator(),
std::back_inserter(cp.columns));
return is;
}
// Type cast operator overload. Cast the type 'Columns' to std::vector<std::string>
operator std::vector<std::string>() const { return columns; }
protected:
// Temporary to hold the read vector
Columns columns{};
};
int main()
{
// Define variable CSV with its range constructor. Read complete CSV in this statement, So, one liner
CSV csv{ std::istream_iterator<ColumnProxy>(testCsv), std::istream_iterator<ColumnProxy>() };
// Print result. Go through all lines and then copy line elements to std::cout
std::for_each(csv.begin(), csv.end(), [](Columns& c) {
std::copy(c.begin(), c.end(), std::ostream_iterator<std::string>(std::cout, " ")); std::cout << "\n"; });
}
我希望解释足够详细,可以让您了解现代 C++ 可以做什么。
这个例子基本上不关心源文本文件中有多少行和列。 它会吃掉一切。
当然,您可以将字符串转换为您需要的任何内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.