有没有办法在C++中实现Python的"separator".join()的模拟?

Is there a way to implement analog of Python's 'separator'.join() in C++?

本文关键字:join separator 模拟 Python C++ 实现 有没有      更新时间:2023-10-16

我只找到了boost::algorithm::string::join。然而,只将Boost用于联接似乎有些过头了。所以也许有一些经过时间考验的食谱?

更新
对不起,问题说明不正确。我正在寻找用分隔符连接字符串的方法,而不仅仅是一个接一个地连接。

由于您正在寻找配方,请继续使用Boost中的配方。一旦你克服了所有的通用性,它就不会太复杂:

  1. 分配一个存储结果的位置
  2. 将序列的第一个元素添加到结果中
  3. 如果还有其他元素,请将分隔符和下一个元素追加到结果中
  4. 返回结果

这是一个在两个迭代器上工作的版本(与Boost版本相反,Boost版本在范围上操作。

template <typename Iter>
std::string join(Iter begin, Iter end, std::string const& separator)
{
  std::ostringstream result;
  if (begin != end)
    result << *begin++;
  while (begin != end)
    result << separator << *begin++;
  return result.str();
}

如果您真的想要''.join(),可以将std::copystd::ostream_iteratorstd::stringstream一起使用。

#include <algorithm> // for std::copy
#include <iterator>  // for std::ostream_iterator
#include <sstream>   // for std::stringstream
std::vector<int> values(); // initialize these
std::stringstream buffer;
std::copy(values.begin(), values.end(), std::ostream_iterator<int>(buffer));

这将向buffer插入所有值。您也可以为std::ostream_iterator指定一个自定义分隔符,但它将被附加在末尾(这是与join的显著区别)。如果您不需要分隔符,这将满足您的需要。

简单地说,其中容器中的类型是int:

std::string s = std::accumulate(++v.begin(), v.end(), std::to_string(v[0]),
                     [](const std::string& a, int b){
                           return a + ", " + std::to_string(b);
                     });

这适用于C++17:

template<class...T>
std::string join(const std::string& sep, T&&...strings) {
    if constexpr(sizeof...(T)) {
        auto t = ((strings + sep) + ...);
        return t.substr(0, t.size() - sep.size());    
    } else {
        return "";
    }
}
int main() {
    std::cout << join(",", "apple", "orange", "banana") << std::endl;
    std::cout << join(",") << std::endl;
}

它应该打印:

apple,orange,banana

C++字符串得到了有效的实现。

std::string s = s1 + s2 + s3;

这可能更快:

std::string str;
str.reserve(total_size_to_concat);
for (std::size_t i = 0; i < s.length(); i++)
{
  str.append(s[i], s[i].length());
}

但这基本上就是编译器对operator+和最小优化所做的,只是猜测要保留的大小
不要害羞。看看字符串的实现。:)

这里有另一个我觉得更方便使用的版本:

std::string join(std::initializer_list<std::string> initList, const std::string& separator = "\")
{
    std::string s;
    for(const auto& i : initList)
    {
        if(s.empty())
        {
            s = i;
        }
        else
        {
            s += separator + i;
        }
    }
    return s;
}

你可以这样称呼它:

join({"C:", "Program Files", "..."});

如果你在项目中使用Qt,你可以直接使用QString的join函数(QString Reference),它可以像python中预期的那样工作。一些例子:

QStringList strList;
qDebug() << strList.join(" and ");

结果:""

strList << "id = 1";
qDebug() << strList.join(" and ");

结果:"id = 1"

strList << "name = me";
qDebug() << strList.join(" and ");

结果:"id = 1 and name = me"

只是另一个简单的解决方案:

template<class T>
std::string str_join(const std::string& delim, const T& items)
{
    std::string s;
    for (const auto& item : items) {
        if (!s.empty()) {
            s += delim;
        }
        s += item;
    }
    return s;
}

对于那些不喜欢begin()end()作为参数并且只喜欢整个容器的人。对于那些不喜欢字符串流而喜欢operator std::string() const的人来说。

用法:

auto s1 = str_join(", ", std::vector<const char*>{"1","2","3"});
struct X
{
    operator std::string() const
    {
        return "X";
    }
};
auto s2 = str_join(":", std::vector<X>{{}, {}, {}});

应使用C++11及更高版本。