请看这里的超简单示例。 (这篇文章的底部有一个更简单的解决方案)
它使用现代 C++ 元素和算法。而且只有几行代码。
#include <iostream>
#include <string>
#include <regex>
#include <iterator>
#include <algorithm>
#include <sstream>
int main() {
// Read a string from the console
if (std::string line{}; std::getline(std::cin, line)) {
// Put the complete line into a std::istringstream
std::istringstream iss{line};
// Print result
std::transform(std::istream_iterator<std::string>(iss), {}, std::ostream_iterator<std::string>(std::cout, " "),
[](const std::string& s) { return std::regex_replace(s, std::regex{ R"([^\d])" }, ""); });
}
return 0;
}
那么,这里发生了什么。让我们逐条来看。所以,首先:
if (std::string line{}; std::getline(std::cin, line)) {
这是带有初始化程序的if-语句。如果您在 C++ 参考 here 中查找 if,那么您会看到,我们现在可以在 if 的第一部分中添加一个额外的初始化语句。我们为什么要使用它?因为它是范围界定的附加措施。变量“line”仅在 if 语句的范围内使用。在 if 之外不需要它。从功能上看,和写法一样:
std::string line{};
if (std::getline(std::cin, line)) {
但是,“line”在 if 语句之外也是可见的。并且,因为我们要防止外部命名空间的污染,所以我们选择了这种方法。
接下来是std::getline。这将从输入流中读取完整的一行,因此,从控制台 (std::cin) 并将其放入字符串中。 std::getline 返回对流的引用。该流有一个重载的 bool 运算符,如果有失败(或文件结尾),则返回。因此,if 语句检查输入操作是否有效。顺便一提。应该检查所有 IO 操作,无论它们是否有效。
好的,现在我们的变量“line”中有完整的用户输入行。
有
std::istringstream iss{line};
我们将字符串放入std::istringstream。我们这样做是因为我们想利用 C++“iostream”库。 std::istringstream 的行为与任何其他流一样,例如std::cin,您可以从中提取由空格分隔的值。比如std::cin >> v1 >> v2。这种方法的缺点是,您需要提前知道值的数量或使用动态增长的容器和循环。
这将 ud 带到了我要解释的下一个构造。您可能听说过“迭代器”。迭代器就像指针,可以指向一系列元素。如果您有std::vector 或任何其他容器,那么您可以使用begin() 和end() 迭代器迭代std::vector 中的所有元素,而不知道std::vector 中有多少元素,而不知道如何它包含许多元素。
对于输入流,我们有类似的东西:std::istream_iterator。此迭代器将遍历std::sitringstream 中的元素,并通过重复调用提取器运算符>> 返回其模板参数中给定的变量类型。在我们的例子中,这里是std::string。你可能知道问:直到什么时候?终点在哪里。如果您查看std::istream_operator 的constructor number 1 的描述,那么您将看到默认构造函数Constructs the end-of-stream iterator。并且可以使用空的大括号 {} 初始化程序生成默认构造。所以 {} 是结束迭代器。
如果我们想从std::istringstream 中读取所有std::strings,那么我们在
std::istream_iterator<std::string>(iss) and {}。所以std::istringstream中的每个字符串。
好的,接下来,有一个类似的输出,std::ostream_iterator。这将为给定范围内的所有元素调用插入器运算符“std::cout,另外还有一个分隔符字符串,它将附加到输出值。
好的,接下来:std::transform。顾名思义,它将把begin() 和end() 迭代器之间的元素范围内的元素转换为另一个范围。因此,它将从std::istringstream 转换如上所示的元素并将它们发送到std::ostream 迭代器。所以,我们读取源值,转换它,然后写入它。
但是,如何转换。对于转换,我们给出了一个简单的 lambda 函数,它调用了std::regex_replace 函数。这是一个标准函数,用其他字符串数据替换字符串的一部分。并且,将被替换的内容由std::regex 指定。这是一种以某种元语言定义并匹配字符串的指定部分的特殊模式。在我们的例子中,我们使用[^\d],这意味着,不是一个数字。您可以测试正则表达式here。您也可以在这里了解它们。
现在,大家一起来解释上述解决方案。
所有这些都可以进一步优化为 2 个语句:
#include <iostream>
#include <string>
#include <regex>
int main() {
// Read a string from the console
if (std::string line{}; std::getline(std::cin, line)) {
// Remove unnecessary characters
std::cout << std::regex_replace(line, std::regex{ R"([^\d ])" }, "") << "\n";
}
return 0;
}
我想不出更简单的解决方案。
如有疑问,请提出。