C++:从文本文件中读入
C++: reading in from a text file
我正试图找出在.txt文件中读取的最佳方式,该文件使用逗号分隔的信息,行分隔。使用这些信息,创建我的股票类的对象。
.txt文件如下所示:
GOOGL, 938.85, 30
APPL, 506.34, 80
MISE, 68.00, 300
我的stock类构造函数类似于stock(字符串符号,双倍价格,int numOfShares(;
在我的主程序中,我想设置一个输入文件流,它将读取信息并创建股票类的对象,比如:
股票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;
}
似乎您想要读取csv数据。这是一个标准的任务,我会给你详细的解释。最后,所有的阅读都将在一行中完成。
我建议使用"现代"C++方法。
仍然所有谈论csv的人都在链接到"我如何在C++中读取和解析csv文件?"?,这些问题来自2009年,现在已经超过10年了。大多数答案也都是陈旧而复杂的。所以,也许是时候改变了。
在现代C++中,您可以使用在范围上迭代的算法。您经常会看到类似"someAlgorim(container.begin((,container.end((,someLambda("的内容。我们的想法是对一些类似的元素进行迭代。
在您的情况下,我们对输入字符串中的标记进行迭代,并创建子字符串。这被称为标记化。
正是出于这个目的,我们有了std::sregex_token_iterator
。因为我们已经为这个目的定义了一些东西,所以我们应该使用它
这是一个迭代器。对于在字符串上迭代,因此为sregex。开始部分定义了我们应该在什么输入范围内操作,然后在输入字符串中有一个std::regex
来表示应该匹配/不应该匹配的内容。匹配策略的类型由最后一个参数给出。
- 1-->给我在正则表达式中定义的内容
- -1-->根据正则表达式告诉我什么是不匹配的
所以,既然我们了解了迭代器,我们就可以std::将令牌从迭代器复制到我们的目标,std::string
的std::vector
。由于我们不知道我们有多少列,我们将使用std::back_inserter
作为目标。这将添加我们从std::sregex_token_iterator
获得的所有令牌,并将其附加到我们的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 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++具有CCD_ 8。这是一个模板,作为模板参数,它获取应该读取的数据类型,作为构造函数参数,它获得对输入流的引用。不管输入流是std::cin
、std::ifstream
还是std::istringstream
。所有类型的流的行为都是相同的。
由于我们没有SO文件,我使用(在下面的示例中(std::istringstream
来存储输入的csv文件。但是,您当然可以通过定义std::ifstream testCsv(filename)
来打开一个文件。没问题。
使用std::istream_iterator
,我们对输入进行迭代并读取类似的数据。在我们的例子中,一个问题是我们希望迭代特殊数据,而不是某些内置数据类型。
为了解决这个问题,我们定义了一个Proxy类,它为我们做内部工作(我们不想知道如何封装在代理中(。在代理中,我们覆盖类型转换运算符,以将结果转换为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"(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"; });
}
我希望这个解释足够详细,能给你一个想法,你可以用现代C++做什么。
这个示例基本上不关心源文本文件中有多少行和列。它会吃掉所有东西。
- 文本文件中的单词链表
- 在C++程序中输入的文本文件将不起作用,除非文本被复制和粘贴
- 如何将内容数组写入文本文件?
- 无法通过空白将文本文件行分隔为矢量
- 我正在使用嵌套的while循环来解析具有多行的文本文件,但由于某种原因,它只通过第一行,我不知道为什么
- C++将文本文件中的数据读取到结构数组中
- 在指针的帮助下,文本文件中单词的频率
- 将字符指针十六进制转换为字符串并保存在文本文件C++中
- 将值从二维数组输出到文本文件
- 如何使用 c++ 实现并发文件/文本编辑?
- 我需要转换一些代码,以便它适用于输入和输出文件文本
- 通过套接字发送二进制文件.文本文件有效,其他文件无效
- 将大型数据文件拆分为多个小文件(文本格式)
- 如何读取输入文件(文本文件)并将输入验证为有效整数
- 从文本文件/文本流中读取Q字符串错误
- 从文本文件(文本解析器)C++中读取行和列的逻辑
- 如何将*.css文件(文本文件)的内容与附加信息存储在新文件中?
- 关于内存映射文件和使用大文件文本编辑器
- 如何在读取文件文本时跳过"n"
- 使用libzip从.zip获取文件(文本除外)