c++ - 如何创建一个函数以从 C++ 中的文本文件中读取并忽略某些字符并将数据值存储在适当的变量中?
问题描述
我的class date
定义如下。我已经对函数write(ostream &o)
进行了编程,以这种格式写入日期,27/May/2020
以便用户轻松阅读。现在我想从包含上述格式日期的文件中读取。那么我如何在 C++ 中编写一个忽略'/'
字符并存储值的函数27
,例如 in int day
、"May"
instring month
和 in 的2010
值int year
。另外,我如何设计一个函数来忽略某个字符并能够将值存储在 C++ 中的各种数据类型中?
class date{
private :
int day;
string month;
int year;
public:
date()
{
day=0;
month="NULL";
year=2020;
}
void getdate()
{
cout<<"Enter Date : ";
cin>>day>>month;
}
void write(ostream &o) const // to write text file or screen
{
o<<day<<'/'<<month.c_str()<<'/'<<year;
}
};
解决方案
您可以使用一种非常简单的方法,使用流提取/插入运算符的属性返回对流的引用。有了它,您可以像在您的示例中那样链接提取语句cin>>day>>month;
。
有了它,你可以做一个单行,如下例所示:
#include <iostream>
#include <string>
#include <sstream>
std::istringstream iss{"27/May/2020"};
int main() {
int day{};
std::string month{};
int year{};
char slash{};
if ((std::getline(iss >> day >> slash, month, '/') >> year) && (slash == '/')) {
std::cout << day << ' ' << month << ' ' << year << '\n';
}
else {
std::cerr << "\nError: Wrong input format\n";
}
return 0;
}
编辑
OP 要求对一个班轮进行更多解释。
if ((std::getline(iss >> day >> slash, month, '/') >> year) && (slash == '/')) {
好的。如您所知,嵌套函数将从内到外进行评估。如果你有例如:pow(sqrt(x+100),y+2)
,那么首先 x+100,然后是 sqrt,然后是 y+2,最后是 pow 将被评估。很好,明白了。
看着我们的一个班轮,这意味着,首先iss >> day
将被评估。在我们开头的公开流 iss(您也可以使用std::cin
or anstd::ifstream
或基本上 any )中:“27/May/2020”。istream
上面的提取操作将提取数字 2 和 7 并将其转换为整数 27。那是我们的一天。
现在,输入流包含“/May/2020”,因为 2 和 7 已被提取。
手术iss >> day
现已结束。重载的提取器运算符>>
将始终返回对istream
调用它的引用。意思是,iss >> day
将返回“iss”。
这种算子的原型通常定义为
std::istream& operator >> (std::istream& is, SomeClass& sc) {
// Do something with SomeClass sc . . .
return is; }
您可以看到,流已返回。这使您可以链接提取器运算符。在评估完第一个iss >> day
语句之后,该行的其余部分将如下所示:
if ((std::getline(iss >> slash, month, '/') >> year) && (slash == '/'))
因为iss >> day
已经完成并返回了iss。请记住,我们的流现在包含“/May/2020”。并且,接下来我们将做:iss >> slash
. 意思是,我们将从流中提取斜杠并将其存储在变量斜杠中。提取器运算符将再次返回 iss。流中的数据现在是“May/2020”。新的一个班轮将是:
if ((std::getline(iss, month, '/') >> year) && (slash == '/'))
这我们可以理解。std::getline will extract a string from the stream, until it sees a slash. That string is stored in the variable month. The data in the stream will now be "2020". And guest what?
std::getline```` 也将返回对给定流的引用。这样一来,一个班轮现在将是
if ((iss >> year) && (slash == '/'))
我们知道,现在会发生什么:iss >> year
将被评估。“2020”将转换为整数 2020。流现在为空。一切都被提取出来了。操作员>>
将再次返回 iss。给我们:
if ((iss) && (slash == '/'))
嗯,if
需要一个布尔表达式,这如何与 iss 一起工作?可以解释:如果需要一个布尔表达式,并且非常聪明,需要一个流作为重载的布尔运算符。请阅读这里。如果流处于良好状态或失败,则此运算符返回。
如果整个链中的某些提取操作失败,则 bool 运算符将返回 false。然后我们会显示错误信息。
作为附加检查,我们验证变量“slash”的内容。
就是这样了。我希望,我可以用一种可以理解的方式来解释它。
结束编辑
但也许它更安全,如果你先阅读完整的一行 ( std::string
withstd::getline
然后拆分这一行。
我还将向您展示一些示例代码。
查看拆分字符串的一些常见模式:
将字符串拆分为标记是一项非常古老的任务。有许多可用的解决方案。都有不同的属性。有些难以理解,有些难以开发,有些更复杂,更慢或更快或更灵活或不灵活。
备择方案
- 手工制作,许多变体,使用指针或迭代器,可能难以开发且容易出错。
- 使用旧式
std::strtok
功能。也许不安全。也许不应该再使用了 std::getline
. 最常用的实现。但实际上是一种“误用”,并没有那么灵活- 使用专门为此目的开发的专用现代功能,最灵活且最适合 STL 环境和算法环境。但是比较慢。
请在一段代码中查看 4 个示例。
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <regex>
#include <algorithm>
#include <iterator>
#include <cstring>
#include <forward_list>
#include <deque>
using Container = std::vector<std::string>;
std::regex delimiter{ "," };
int main() {
// Some function to print the contents of an STL container
auto print = [](const auto& container) -> void { std::copy(container.begin(), container.end(),
std::ostream_iterator<std::decay<decltype(*container.begin())>::type>(std::cout, " ")); std::cout << '\n'; };
// Example 1: Handcrafted -------------------------------------------------------------------------
{
// Our string that we want to split
std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
Container c{};
// Search for comma, then take the part and add to the result
for (size_t i{ 0U }, startpos{ 0U }; i <= stringToSplit.size(); ++i) {
// So, if there is a comma or the end of the string
if ((stringToSplit[i] == ',') || (i == (stringToSplit.size()))) {
// Copy substring
c.push_back(stringToSplit.substr(startpos, i - startpos));
startpos = i + 1;
}
}
print(c);
}
// Example 2: Using very old strtok function ----------------------------------------------------------
{
// Our string that we want to split
std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
Container c{};
// Split string into parts in a simple for loop
#pragma warning(suppress : 4996)
for (char* token = std::strtok(const_cast<char*>(stringToSplit.data()), ","); token != nullptr; token = std::strtok(nullptr, ",")) {
c.push_back(token);
}
print(c);
}
// Example 3: Very often used std::getline with additional istringstream ------------------------------------------------
{
// Our string that we want to split
std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
Container c{};
// Put string in an std::istringstream
std::istringstream iss{ stringToSplit };
// Extract string parts in simple for loop
for (std::string part{}; std::getline(iss, part, ','); c.push_back(part))
;
print(c);
}
// Example 4: Most flexible iterator solution ------------------------------------------------
{
// Our string that we want to split
std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
Container c(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {});
//
// Everything done already with range constructor. No additional code needed.
//
print(c);
// Works also with other containers in the same way
std::forward_list<std::string> c2(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {});
print(c2);
// And works with algorithms
std::deque<std::string> c3{};
std::copy(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {}, std::back_inserter(c3));
print(c3);
}
return 0;
}
推荐阅读
- c# - 从 API 获取大量数据会返回错误
- azure-cognitive-search - 在 Azure 搜索中返回页码?
- python - 当我设置标记大小或不透明度时,3D 散点不会显示在 Plotly 上
- javascript - 'passport.authenticate("google")' - React 客户端不重定向
- python - 为什么我在阅读时不能使用路径
- python - Jython 2.7.1 的相关 Python 版本是什么?
- python - Python - 删除字符数少于“x”的行,同时保留空白行
- arduino - 如何修复 arduino 变量在回调函数中获取额外数据?
- python - 为什么 Databricks Python 无法从我的 Azure Datalake Storage Gen1 读取?
- .htaccess - 使缓存的 .htaccess 文件无效