ezp*_*sso 69 c++ string stl stdstring implode
我正在寻找最优雅的方法将字符串向量内部转换为字符串.以下是我现在使用的解决方案:
static std::string& implode(const std::vector<std::string>& elems, char delim, std::string& s)
{
for (std::vector<std::string>::const_iterator ii = elems.begin(); ii != elems.end(); ++ii)
{
s += (*ii);
if ( ii + 1 != elems.end() ) {
s += delim;
}
}
return s;
}
static std::string implode(const std::vector<std::string>& elems, char delim)
{
std::string s;
return implode(elems, delim, s);
}
Run Code Online (Sandbox Code Playgroud)
那里还有其他人吗?
And*_*ner 120
用途boost::algorithm::join(..):
#include <boost/algorithm/string/join.hpp>
...
std::string joinedString = boost::algorithm::join(elems, delim);
Run Code Online (Sandbox Code Playgroud)
另见这个问题.
seh*_*ehe 24
std::vector<std::string> strings;
const char* const delim = ", ";
std::ostringstream imploded;
std::copy(strings.begin(), strings.end(),
std::ostream_iterator<std::string>(imploded, delim));
Run Code Online (Sandbox Code Playgroud)
(包括<string>,<vector>,<sstream>和<iterator>)
Joh*_*nck 21
您应该使用std::ostringstream而不是std::string构建输出(然后您可以str()在最后调用其方法来获取字符串,因此您的接口不需要更改,只需要临时s).
从那里,您可以改为使用std::ostream_iterator,如下所示:
copy(elems.begin(), elems.end(), ostream_iterator<string>(s, delim));
Run Code Online (Sandbox Code Playgroud)
但这有两个问题:
delim现在需要是一个const char*,而不是一个char.没什么大不了.std::ostream_iterator在每个元素之后写入分隔符,包括最后一个元素.因此,你需要在最后删除最后一个,或者编写你自己的迭代器版本,这个版本没有这个烦恼.如果你有很多需要这样的东西的代码,那么值得做后者; 否则可能最好避免整个混乱(即使用ostringstream但不是ostream_iterator).小智 13
我喜欢使用这种单行累加(无尾随分隔符):
std::accumulate(
std::next(elems.begin()),
elems.end(),
elems[0],
[](std::string a, std::string b) {
return a + delimiter + b;
}
);
Run Code Online (Sandbox Code Playgroud)
小智 12
简单的愚蠢解决方案怎么样?
std::string String::join(const std::vector<std::string> &lst, const std::string &delim)
{
std::string ret;
for(const auto &s : lst) {
if(!ret.empty())
ret += delim;
ret += s;
}
return ret;
}
Run Code Online (Sandbox Code Playgroud)
Gus*_*uss 11
因为我喜欢单行(它们对于各种奇怪的东西非常有用,正如你最后会看到的),这里有一个使用std :: accumulate和C++ 11 lambda的解决方案:
std::accumulate(alist.begin(), alist.end(), std::string(),
[](const std::string& a, const std::string& b) -> std::string {
return a + (a.length() > 0 ? "," : "") + b;
} )
Run Code Online (Sandbox Code Playgroud)
我觉得这个语法对于stream运算符很有用,我不希望在流操作的范围之外有各种奇怪的逻辑,只是做一个简单的字符串连接.例如,考虑使用流操作符格式化字符串的方法的return语句(使用std;):
return (dynamic_cast<ostringstream&>(ostringstream()
<< "List content: " << endl
<< std::accumulate(alist.begin(), alist.end(), std::string(),
[](const std::string& a, const std::string& b) -> std::string {
return a + (a.length() > 0 ? "," : "") + b;
} ) << endl
<< "Maybe some more stuff" << endl
)).str();
Run Code Online (Sandbox Code Playgroud)
string join(const vector<string>& vec, const char* delim)
{
stringstream res;
copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim));
return res.str();
}
Run Code Online (Sandbox Code Playgroud)
特别是对于较大的集合,您希望避免检查您是否仍在添加第一个元素以确保没有尾随分隔符...
所以对于空或单元素列表,根本没有迭代。
空范围是微不足道的:返回“”。
可以通过以下方式完美处理单个元素或多元素accumulate:
auto join = [](const auto &&range, const auto separator) {
if (range.empty()) return std::string();
return std::accumulate(
next(begin(range)), // there is at least 1 element, so OK.
end(range),
range[0], // the initial value
[&separator](auto result, const auto &value) {
return result + separator + value;
});
};
Run Code Online (Sandbox Code Playgroud)
运行示例(需要 C++14):http : //cpp.sh/8uspd
虽然我通常会建议按照最佳答案使用 Boost,但我认识到在某些项目中这是不需要的。
建议使用的 STL 解决方案std::ostream_iterator不会按预期工作 - 它会在末尾附加一个分隔符。
现在有一种方法可以使用std::experimental::ostream_joiner使用现代 C++ 来做到这一点:
std::ostringstream outstream;
std::copy(strings.begin(),
strings.end(),
std::experimental::make_ostream_joiner(outstream, delimiter.c_str()));
return outstream.str();
Run Code Online (Sandbox Code Playgroud)
小智 6
使用 fmt 你可以做到。
#include <fmt/format.h>
auto s = fmt::format("{}",fmt::join(elems,delim));
Run Code Online (Sandbox Code Playgroud)
但我不知道 join 是否会使其成为 std::format。
这在 C++23 中得到了方便的一句:
auto str = std::ranges::fold_left(elems | std::views::join_with(delim), std::string{}, std::plus<>{});
Run Code Online (Sandbox Code Playgroud)
另一个简单而好的解决方案是使用range v3。当前版本是 C++14 或更高版本,但也有旧版本是 C++11 或更高版本。不幸的是,C++20 范围没有该intersperse功能。
这种方法的好处是:
功能分解(参考):
accumulate= 与此类似,std::accumulate但参数是范围和初始值。还有一个可选的第三个参数,它是运算符函数。filter= 与 一样std::filter,过滤掉不符合谓词的元素。intersperse= 关键功能!在范围输入元素之间散布分隔符。#include <iostream>
#include <string>
#include <vector>
#include <range/v3/numeric/accumulate.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/intersperse.hpp>
int main()
{
using namespace ranges;
// Can be any std container
std::vector<std::string> a{ "Hello", "", "World", "is", "", "a", "program" };
std::string delimiter{", "};
std::string finalString =
accumulate(a | views::filter([](std::string s){return !s.empty();})
| views::intersperse(delimiter)
, std::string());
std::cout << finalString << std::endl; // Hello, World, is, a, program
}
Run Code Online (Sandbox Code Playgroud)
编辑:正如@Franklin Yu建议的那样,只能将 std 库与std::ranges::views::join_with一起使用。但不幸的是仅适用于 c++23。由于我们使用的是 c++23,我们还可以使用std::ranges::fold_left来std::accumulate创建单行表达式。std::ranges::fold_left是 rages v3 的标准版本rages::accumulate。
#include <iostream>
#include <string>
#include <vector>
#include <ranges>
#include <algorithm>
int main()
{
// Can be any std container
std::vector<std::string> a{ "Hello", "", "World", "is", "", "a", "program" };
std::string delimiter{", "};
std::string finalString =
std::ranges::fold_left(a | std::views::filter([](std::string s){return !s.empty();})
| std::views::join_with(delimiter)
, std::string()
, std::plus());
std::cout << finalString << std::endl; // Hello, World, is, a, program
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
71782 次 |
| 最近记录: |