C++ C++;:从文本文件读入
我正试图找出在.txt文件中读取信息的最佳方式,信息以逗号分隔,行分隔。使用该信息,创建我的股票类的对象 .txt文件如下所示:C++ C++;:从文本文件读入,c++,ifstream,C++,Ifstream,我正试图找出在.txt文件中读取信息的最佳方式,信息以逗号分隔,行分隔。使用该信息,创建我的股票类的对象 .txt文件如下所示: GOOGL, 938.85, 30 APPL, 506.34, 80 MISE, 68.00, 300 我的stock类构造函数类似于stock(字符串符号,双倍价格,int numOfShares) 在我的主程序中,我想设置一个输入文件流,它将读取信息并创建stock类的对象,如下所示: 股票1(“GOOGL”,938.85,30) 股票2
GOOGL, 938.85, 30
APPL, 506.34, 80
MISE, 68.00, 300
我的stock类构造函数类似于stock(字符串符号,双倍价格,int numOfShares)
在我的主程序中,我想设置一个输入文件流,它将读取信息并创建stock类的对象,如下所示:
股票1(“GOOGL”,938.85,30)
股票2(“APPL”,380.50,60)
我假设我使用ifstream和getline,但不太确定如何设置它
谢谢 #包括
#include <fstream>
#include <string>
#include <sstream>
int main()
{
//Open file
std::ifstream file("C:\\Temp\\example.txt");
//Read each line
std::string line;
while (std::getline(file, line))
{
std::stringstream ss(line);
std::string symbol;
std::string numstr;
//Read each comma delimited string and convert to required type
std::getline(ss, symbol, ',');
std::getline(ss, numstr, ',');
double price = std::stod(numstr);
std::getline(ss, numstr, ',');
int numOfShares = std::stoi(numstr);
//Construct stock object with variables above
stock mystock(symbol, price, numOfShares);
}
return 0;
}
#包括
#包括
int main()
{
//打开文件
std::ifstream文件(“C:\\Temp\\example.txt”);
//读每一行
std::字符串行;
while(std::getline(文件,行))
{
std::stringstream ss(线路);
字符串符号;
std::字符串numstr;
//读取每个逗号分隔的字符串并转换为所需类型
std::getline(ss,符号“,”);
std::getline(ss,numstr,',');
双倍价格=std::stod(numstr);
std::getline(ss,numstr,',');
int numOfShares=std::stoi(numstr);
//使用上面的变量构造stock对象
股票mystock(符号、价格、numOfShares);
}
返回0;
}
您似乎想要读取csv数据。这是一个标准的任务,我会给你详细的解释。最后,所有读数将在一行中完成
我建议使用一种“现代”C++方法。< /P>
尽管如此,所有谈论csv的人都链接到了,这些问题是从2009年开始的,现在已经超过10年了。大多数答案都是老生常谈的,而且非常复杂。所以,也许是时候改变了
在现代C++中,你有迭代遍历范围的算法。您经常会看到类似“someAlgoritm(container.begin()、container.end()、someLambda)”的内容。我们的想法是迭代一些类似的元素
在您的例子中,我们迭代输入字符串中的标记,并创建子字符串。这称为标记化
正是出于这个目的,我们有了std::sregex\u令牌\u迭代器
。因为我们有这样的定义,我们应该使用它
这是一个迭代器。用于在字符串上迭代,因此是sregex。开始部分定义了我们将在什么范围内操作输入,然后在输入字符串中有一个std::regex
来表示应该匹配的内容或不应该匹配的内容。最后一个参数给出了匹配策略的类型
- 1-->给我在正则表达式中定义的内容,然后
- -1-->根据正则表达式给出不匹配的内容
std::string
的std::vector
。由于我们不知道我们有多少列,我们将使用std::back_inserter
作为目标。这将添加我们从std::sregex\u token\u迭代器
获得的所有令牌,并将其附加到我们的std::vector>
中。我们有多少专栏并不重要
好。这样的声明可能看起来像
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 comma
-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::istRAMMyTyror < /C>。这是一个模板,作为模板参数,它获取应该读取的数据类型,作为构造函数参数,它获取对输入流的引用。如果输入流是std::cin
,或std::ifstream
或std::istringstream
,则无所谓。所有类型的流的行为都是相同的
由于我们没有SO文件,所以(在下面的示例中)我使用std::istringstream
来存储输入的csv文件。当然,您可以通过定义std::ifstream testCsv(filename)
来打开文件。没问题
使用std::istream_迭代器,我们对输入进行迭代并读取类似的数据。在我们的例子中,一个问题是我们想要迭代特殊数据,而不是某些内置数据类型
为了解决这个问题,我们定义了一个代理类,它为我们完成内部工作(我们不想知道如何封装在代理中)。在代理中,我们覆盖类型转换操作符,以获得std::istream\u迭代器的预期类型的结果
最后一个重要步骤。std::vector
具有范围构造函数。它还有许多其他构造函数,我们可以在定义std::vector
类型的变量时使用它们。但就我们的目的而言,这个构造函数最适合我们
所以我们定义一个变量csv,并使用它的范围构造函数,给它一个范围的开始和结束。在我们的具体示例中,我们使用std::istream\u迭代器的开始迭代器和结束迭代器
如果我们结合以上所有内容,读取完整的CSV文件是一个线性过程,它是一个变量的定义,并调用其构造函数
请参阅生成的代码:
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <regex>
#include <algorithm>
std::istringstream testCsv{ R"(GOOGL, 938.85, 30
APPL, 506.34, 80
MISE, 68.00, 300)" };
// 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"; });
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
std::istringstream testCsv{R”(古格尔,938.85,30
应用,506.34,80
米塞,68.00,300;
//定义别名以便于阅读
使用Columns=std::vector;
使用CSV=std::vector;
//输入迭代器的代理
结构列代理{
//重载提取器。读取完整的行
friend std::istream&operator>>(std::istream&is、ColumnProxy&cp){
//读一行
std::string行;cp.columns.clear();
std::getline(is,line);
//分隔符
常量std::正则表达式re(“,”);
//分割v