如何将字符串向量内嵌到字符串中(优雅的方式)

时间:2011-04-16 19:29:02

标签: 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);
}

那里还有其他人吗?

20 个答案:

答案 0 :(得分:116)

使用boost::algorithm::join(..)

#include <boost/algorithm/string/join.hpp>
...
std::string joinedString = boost::algorithm::join(elems, delim);

另见this question

答案 1 :(得分: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));

(包括<string><vector><sstream><iterator>

<强> If you want to have a clean end (no trailing delimiter) have a look here

答案 2 :(得分:20)

您应该使用std::ostringstream而不是std::string来构建输出(然后您可以在最后调用其str()方法来获取字符串,因此您的界面无需更改,仅临时s)。

从那里,您可以更改为使用std::ostream_iterator,如下所示:

copy(elems.begin(), elems.end(), ostream_iterator<string>(s, delim)); 

但这有两个问题:

  1. delim现在必须是const char*,而不是单char。没什么大不了的。
  2. std::ostream_iterator在每个元素之后写入分隔符,包括最后一个元素。因此,你需要在最后删除最后一个,或者编写你自己的迭代器版本,这个版本没有这个烦恼。如果你有很多代码需要这样的东西,那么值得做后者;否则可能最好避免整个混乱(即使用ostringstream而不是ostream_iterator)。

答案 3 :(得分:10)

因为我喜欢单行(它们对各种奇怪的东西非常有用,你最后会看到),这里有一个使用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; 
    } )

我觉得这个语法对于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();

答案 4 :(得分:8)

string join(const vector<string>& vec, const char* delim)
{
    stringstream res;
    copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim));
    return res.str();
}

答案 5 :(得分:6)

有了 fmt,你就可以做到。

#include <fmt/format.h>
auto s = fmt::format("{}",fmt::join(elems,delim)); 

但我不知道 join 是否会使其成为 std::format。

答案 6 :(得分:3)

使用std::accumulate的版本:

#include <numeric>
#include <iostream>
#include <string>

struct infix {
  std::string sep;
  infix(const std::string& sep) : sep(sep) {}
  std::string operator()(const std::string& lhs, const std::string& rhs) {
    std::string rz(lhs);
    if(!lhs.empty() && !rhs.empty())
      rz += sep;
    rz += rhs;
    return rz;
  }
};

int main() {
  std::string a[] = { "Hello", "World", "is", "a", "program" };
  std::string sum = std::accumulate(a, a+5, std::string(), infix(", "));
  std::cout << sum << "\n";
}

答案 7 :(得分:3)

特别是对于更大的集合,您希望避免检查是否仍然添加第一个元素以确保没有尾随分隔符...

因此对于空元素或单元素列表,根本没有迭代。

空的范围是微不足道的:返回&#34;&#34;。

单个元素或多元素可以通过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;
         });
};

运行示例(需要C ++ 14 ):http://cpp.sh/8uspd

答案 8 :(得分:3)

简单的愚蠢解决方案呢?

set -x
a="My name is Vikas"  
b="${a} Singh"
set +x

答案 9 :(得分:2)

这是另一个在最后一个元素之后不添加分隔符的方法:

std::string concat_strings(const std::vector<std::string> &elements,
                           const std::string &separator)
{       
    if (!elements.empty())
    {
        std::stringstream ss;
        auto it = elements.cbegin();
        while (true)
        {
            ss << *it++;
            if (it != elements.cend())
                ss << separator;
            else
                return ss.str();
        }       
    }
    return "";

答案 10 :(得分:2)

我喜欢使用这种单线累积(没有尾随定界符):

std::accumulate(
    std::next(elems.begin()), 
    elems.end(), 
    elems[0], 
    [](std::string a, std::string b) {
        return a + delimiter + b;
    }
);

答案 11 :(得分:2)

将此answer的一部分用于另一个问题,根据没有尾随逗号的分隔符,您可以加入此

用法:

std::vector<std::string> input_str = std::vector<std::string>({"a", "b", "c"});
std::string result = string_join(input_str, ",");
printf("%s", result.c_str());
/// a,b,c

代码:

std::string string_join(const std::vector<std::string>& elements, const char* const separator)
{
    switch (elements.size())
    {
        case 0:
            return "";
        case 1:
            return elements[0];
        default:
            std::ostringstream os;
            std::copy(elements.begin(), elements.end() - 1, std::ostream_iterator<std::string>(os, separator));
            os << *elements.rbegin();
            return os.str();
    }
}

答案 12 :(得分:1)

另一个简单而好的解决方案是使用 ranges v3。当前版本为 C++14 或更高版本,但也有 C++11 或更高版本的旧版本。不幸的是,C++20 范围没有 intersperse 函数。

这种方法的好处是:

  • 优雅
  • 轻松处理空字符串
  • 处理列表的最后一个元素
  • 效率。因为范围是惰性计算的。
  • 小而有用的库

功能细分(Reference):

  • 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
}

答案 13 :(得分:1)

首先,这里需要一个流类ostringstream来连接多次并保存过多内存分配的潜在麻烦。

代码:

string join(const vector<string>& vec, const char* delim)
{
    ostringstream oss;
    if(!string_vector.empty()) {
        copy(string_vector.begin(),string_vector.end() - 1, ostream_iterator<string>(oss, delim.c_str()));
    }
    return oss.str();
}

vector<string> string_vector {"1", "2"};
string delim("->");
string joined_string = join();  // get "1->2"

说明:

在思考时,请将oss视为std::cout

当我们想写:

std::cout << string_vector[0] << "->" << string_vector[1] << "->"

我们可以使用以下STL类作为帮助:

每次使用ostream_iterator时,

<<都会返回一个自动附加分隔符的包装输出流。

例如,

ostream my_cout = ostream_iterator<string>(std::cout, "->")

std:cout包裹为my_cout

所以每次my_cout << "string_vector[0]"

表示std::cout << "string_vector[0]" << "->"

至于 copy(vector.begin(), vector.end(), std::out);

意味着 std::cout << vector[0] << vector[1] (...) << vector[end]

答案 14 :(得分:1)

这是我使用的,简单而灵活的

string joinList(vector<string> arr, string delimiter)
{
    if (arr.empty()) return "";

    string str;
    for (auto i : arr)
        str += i + delimiter;
    str = str.substr(0, str.size() - delimiter.size());
    return str;
}

使用:

string a = joinList({ "a", "bbb", "c" }, "!@#");

输出:

a!@#bbb!@#c

答案 15 :(得分:1)

添加!! String s =“”;

for (int i = 0; i < doc.size(); i++)   //doc is the vector
    s += doc[i];

答案 16 :(得分:1)

稍长的解决方案,但不使用std::ostringstream,并且不需要黑客来删除最后一个分隔符。

http://www.ideone.com/hW1M9

代码:

struct appender
{
  appender(char d, std::string& sd, int ic) : delim(d), dest(sd), count(ic)
  {
    dest.reserve(2048);
  }

  void operator()(std::string const& copy)
  {
    dest.append(copy);
    if (--count)
      dest.append(1, delim);
  }

  char delim;
  mutable std::string& dest;
  mutable int count;
};

void implode(const std::vector<std::string>& elems, char delim, std::string& s)
{
  std::for_each(elems.begin(), elems.end(), appender(delim, s, elems.size()));
}

答案 17 :(得分:0)

试试这个,但使用vector而不是list

template <class T>
std::string listToString(std::list<T> l){
    std::stringstream ss;
    for(std::list<int>::iterator it = l.begin(); it!=l.end(); ++it){
        ss << *it;
        if(std::distance(it,l.end())>1)
            ss << ", ";
    }
    return "[" + ss.str()+ "]";
}

答案 18 :(得分:0)

使用三元运算符?:的可能解决方案。

std::string join(const std::vector<std::string> & v, const std::string & delimiter = ", ") {
    std::string result;

    for (size_t i = 0; i < v.size(); ++i) {
        result += (i ? delimiter : "") + v[i]; 
    }

    return result;
}

join({"2", "4", "5"})会给您2, 4, 5

答案 19 :(得分:0)

这个可以用boost解决

#include <boost/range/adaptor/filtered.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/algorithm.hpp>

std::vector<std::string> win {"Stack", "", "Overflow"};
const std::string Delimitor{","};

const std::string combined_string = 
                  boost::algorithm::join(win |
                         boost::adaptors::filtered([](const auto &x) {
                                                      return x.size() != 0;
                                                      }), Delimitor);

Output:

combined_string: "Stack,Overflow"