网站首页 > 技术文章 正文
有时,库中没有算法可以完成手头的任务。我们可以使用迭代器,使用与算法库相同的技术,轻松编写一个。
例如,我们经常需要将容器中的元素用分隔符连接成字符串。一个常见的解决方案是使用一个简单的 for() 循环:
for(auto v : c) cout << v << ', ';
这个简单解决方案的问题是它留下了一个尾随分隔符:
vector<string> greek{ "alpha", "beta", "gamma", "delta", "epsilon" };
for(auto v : greek) cout << v << ", ";
cout << '\n';
输出:
alpha, beta, gamma, delta, epsilon,
这在测试环境中可能没问题,但在任何生产系统中,那个尾随的逗号都是不可接受的。
ranges::views 库有一个 join() 函数,但它没有提供分隔符:
auto greek_view = views::join(greek);
views::join() 函数返回一个 ranges::view 对象。这需要一个单独的步骤来显示或转换为字符串。我们可以使用 for() 循环遍历视图:
for(const char c : greek_view) cout << c;
cout << '\n';
输出看起来像这样:
alphabetagammadeltaepsilon
它都在那里,但我们需要在元素之间正确地分隔,以使其对我们的目的有用。
由于算法库中没有满足我们需求的函数,我们将编写一个。
如何做到这一点…
对于这个食谱,我们将把容器的元素连接成一个带有分隔符的字符串:
在我们的 main() 函数中,我们声明一个字符串向量:
int main() {
vector<string> greek{ "alpha", "beta", "gamma", "delta", "epsilon" };
...
}
现在,让我们编写一个简单的 join() 函数,使用 ostream 对象将元素用分隔符连接起来:
namespace bw {
template<typename I>
ostream& join(I it, I end_it, ostream& o,
string_view sep = "") {
if(it != end_it) o << *it++;
while(it != end_it) o << sep << *it++;
return o;
}
}
我把这放在我自己的 bw 命名空间中,以避免名称冲突。
我们可以用 cout 这样调用它:
bw::join(greek.begin(), greek.end(), cout, ", ") << '\n';
因为它返回 ostream 对象,我们可以在它后面加上 << 向流中添加一个换行符。
输出:
alpha, beta, gamma, delta, epsilon
我们通常想要一个字符串,而不是直接写入 cout。我们可以为返回字符串对象的版本重载这个函数:
template<typename I>
string join(I it, I end_it, string_view sep = "") {
ostringstream ostr;
join(it, end_it, ostr, sep);
return ostr.str();
}
这也在 bw 命名空间中。这个函数创建了一个 ostringstream 对象来传递给 bw::join() 的 ostream 版本。它从 ostringstream 对象的 str() 方法返回一个字符串对象。
我们可以使用它:
string s = bw::join(greek.begin(), greek.end(), ", ");
cout << s << '\n';
输出:
alpha, beta, gamma, delta, epsilon
让我们添加一个最终的重载,使这个更容易使用:
string join(const auto& c, string_view sep = "") {
return join(begin(c), end(c), sep);
}
这个版本只接受一个容器和一个分隔符,这应该能满足大多数用例:
string s = bw::join(greek, ", ");
cout << s << '\n';
输出:
alpha, beta, gamma, delta, epsilon
它是如何工作的…
这个食谱的大部分工作由迭代器和 ostream 对象完成:
namespace bw {
template<typename I>
ostream& join(I it, I end_it, ostream& o,
string_view sep = "") {
if(it != end_it) o << *it++;
while(it != end_it) o << sep << *it++;
return o;
}
}
分隔符在第一个元素之后,每个连续元素之间,最后一个元素之前停止。这意味着我们可以在每个元素之前添加一个分隔符,跳过第一个,或者在每个元素之后添加一个分隔符,跳过最后一个。如果我们测试并跳过第一个元素,逻辑会更简单。我们在 while() 循环之前的一行就是这样做的:
if(it != end_it) o << *it++;
一旦我们把第一个元素排除在外,我们可以简单地在每个剩余元素之前添加一个分隔符:
while(it != end_it) o << sep << *it++;
我们返回 ostream 对象作为方便。这允许用户轻松地向流中添加换行符或其他对象:
bw::join(greek.begin(), greek.end(), cout, ", ") << '\n';
输出:
alpha, beta, gamma, delta, epsilon
还有更多…
与任何库算法一样,join() 函数将适用于支持前向迭代器的任何容器。例如,这是来自 numbers 库的双精度常数列表:
namespace num = std::numbers;
list<double> constants { num::pi, num::e, num::sqrt2 };
cout << bw::join(constants, ", ") << '\n';
输出:
3.14159, 2.71828, 1.41421
它甚至可以与 ranges::view 对象一起使用,比如前面食谱中定义的 greek_view:
cout << bw::join(greek_view, ":") << '\n';
输出:
a:l:p:h:a:b:e:t:a:g:a:m:m:a:d:e:l:t:a:e:p:s:i:l:o:n
猜你喜欢
- 2024-10-11 c++ 编译阶段(c++编译原理)
- 2024-10-11 C++代码解析3(c++代码大全及其含义)
- 2024-10-11 Qt/C++中英输入法/嵌入式输入法/小数字面板/简繁切换/特殊字符
- 2024-10-11 深入解析C++字符串处理:高效去除头尾空格的多种方法与实践
- 2024-10-11 C到C++转换学习笔记(c和c++转换)
- 2024-10-11 初识C++输入输出(c++基本的输入输出)
- 2024-10-11 如何从C语言快速过渡到C++?大神回答道:只需要一个下午就可以
- 2024-10-11 C++系列2-1:C++快速入门之命名空间和输入输出
- 2024-10-11 EasyC++01,C++程序概述(c++ ecs)
- 2024-10-11 C++第2课:输出与换行#少儿编程#盐城少儿编程#编程机器人
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)