代码之家  ›  专栏  ›  技术社区  ›  Alexis Wilke

仅使用STL与QStringList的join()函数有什么等价关系[[副本]

  •  0
  • Alexis Wilke  · 技术社区  · 6 年前

    我想复制一本书的内容 vector 到一个长的 string 使用自定义分隔符。到目前为止,我试过:

    // .h
    string getLabeledPointsString(const string delimiter=",");
    // .cpp
    string Gesture::getLabeledPointsString(const string delimiter) {
        vector<int> x = getLabeledPoints();
        stringstream  s;
        copy(x.begin(),x.end(), ostream_iterator<int>(s,delimiter));
        return s.str();
    }
    

    但我明白了

    no matching function for call to ‘std::ostream_iterator<int, char, std::char_traits<char> >::ostream_iterator(std::stringstream&, const std::string&)’
    

    我试过了 charT*

    error iso c++ forbids declaration of charT with no type
    

    char ostream_iterator<int>(s,&delimiter) 但是字符串中有奇怪的字符。

    0 回复  |  直到 12 年前
        1
  •  28
  •   jpalecek    12 年前

    Use delimiter.c_str() as the delimiter :

    copy(x.begin(),x.end(), ostream_iterator<int>(s,delimiter.c_str()));
    

    const char* 指向字符串,这是什么 ostream_operator 对你的期望 std::string .

        2
  •  16
  •   max.kondr    8 年前

    C++ 11:

    vector<string> x = {"1", "2", "3"};
    string s = std::accumulate(std::begin(x), std::end(x), string(),
                                    [](string &ss, string &s)
                                    {
                                        return ss.empty() ? s : ss + "," + s;
                                    });
    
        3
  •  13
  •   Shadow2531    12 年前

    另一种方法是:

    #include <iostream>
    #include <string>
    #include <vector>
    #include <sstream>
    using namespace std;
    
    template <typename T>
    string join(const T& v, const string& delim) {
        ostringstream s;
        for (const auto& i : v) {
            if (&i != &v[0]) {
                s << delim;
            }
            s << i;
        }
        return s.str();
    }
    
    int main() {
        cout << join(vector<int>({1, 2, 3, 4, 5}), ",") << endl;
    }
    

    (c++11基于范围的for循环和'auto')

        4
  •  9
  •   Matthieu M.    12 年前
    std::string Gesture::getLabeledPointsString(const std::string delimiter) {
      return boost::join(getLabeledPoints(), delimiter);
    }
    

    getLabeledPointsString 此时;)

        5
  •  9
  •   Matt Balvin    8 年前

    这是对上面已经提供的两个答案的扩展,因为运行时性能似乎是评论中的一个主题。我本想把它添加为评论,但我还没有这个特权。

    我使用Visual Studio 2015测试了两个实现的运行时性能:

    使用stringstream:

    std::stringstream result;
    auto it = vec.begin();
    result << (unsigned short)*it++;
    for (; it != vec.end(); it++) {
        result << delimiter;
        result << (unsigned short)*it;
    }
    return result.str();
    

    使用累加:

    std::string result = std::accumulate(std::next(vec.begin()), vec.end(),
        std::to_string(vec[0]),
        [&delimiter](std::string& a, uint8_t b) {
        return a + delimiter+ std::to_string(b);
    });
    return result;
    

    accumulate实现稍微快一些(20-50ms,在256个元素的向量上进行1000次迭代,大约占总运行时间的10-30%(~180ms))。然而 accumulate a parameter by value导致了类似的运行时差异,这有利于 stringstream 实施。这个 积累 当直接返回结果字符串而不是分配给立即返回的局部变量时,实现也有一些改进。YMV与其他C++编译器。

    调试生成速度比使用 积累 因此,我认为上面几个注释中提到的额外字符串创建是由优化器解决的。

    我正在研究一个使用 vector 属于 uint8_t 价值观。完整的测试代码如下:

    #include <vector>
    #include <iostream>
    #include <sstream>
    #include <numeric>
    #include <chrono>
    
    using namespace std;
    typedef vector<uint8_t> uint8_vec_t;
    
    string concat_stream(const uint8_vec_t& vec, string& delim = string(" "));
    string concat_accumulate(const uint8_vec_t& vec, string& delim = string(" "));
    
    string concat_stream(const uint8_vec_t& vec, string& delimiter)
    {
        stringstream result;
    
        auto it = vec.begin();
        result << (unsigned short)*it++;
        for (; it != vec.end(); it++) {
            result << delimiter;
            result << (unsigned short)*it;
        }
        return result.str();
    }
    
    string concat_accumulate(const uint8_vec_t& vec, string& delimiter)
    {
        return accumulate(next(vec.begin()), vec.end(),
            to_string(vec[0]),
            [&delimiter](string& a, uint8_t b) {
            return a + delimiter + to_string(b);
        });
    }
    
    int main()
    {
        const int elements(256);
        const int iterations(1000);
    
        uint8_vec_t test(elements);
        iota(test.begin(), test.end(), 0);
    
        int i;
        auto stream_start = chrono::steady_clock::now();
        string join_with_stream;
        for (i = 0; i < iterations; ++i) {
            join_with_stream = concat_stream(test);
        }
        auto stream_end = chrono::steady_clock::now();
    
        auto acc_start = chrono::steady_clock::now();
        string join_with_acc;
        for (i = 0; i < iterations; ++i) {
            join_with_acc = concat_accumulate(test);
        }
        auto acc_end = chrono::steady_clock::now();
    
        cout << "Stream Results:" << endl;
        cout << "    elements: " << elements << endl;
        cout << "    iterations: " << iterations << endl;
        cout << "    runtime: " << chrono::duration<double, milli>(stream_end - stream_start).count() << " ms" << endl;
        cout << "    result: " << join_with_stream << endl;
    
        cout << "Accumulate Results:" << endl;
        cout << "    elements: " << elements << endl;
        cout << "    iterations: " << iterations << endl;
        cout << "    runtime: " << chrono::duration<double, milli>(acc_end - acc_start).count() << " ms" << endl;
        cout << "    result:" << join_with_acc << endl;
    
        return 0;
    }
    
        6
  •  2
  •   Glen Knowles    7 年前
    string join(const vector<string> & v, const string & delimiter = ",") {
        string out;
        if (auto i = v.begin(), e = v.end(); i != e) {
            out += *i++;
            for (; i != e; ++i) out.append(delimiter).append(*i);
        }
        return out;
    }
    

    • 不需要额外的条件来避免额外的尾部分隔符
    • 确保向量为空时不会崩溃
        7
  •  1
  •   davnat    4 年前

    我知道这是一个老问题,但我有一个类似的问题,上面的答案没有一个适合我所有的需要,所以我会在这里张贴我的解决办法。

    我的要求是:

    • 我需要一个通用的解决方案,能够处理任何iterable容器和任何数据类型,当然对于自定义数据类型,您必须提供一个合适的 operator<<()
    • int8_t uint8_t char s由 std::stringstream :也许这就是你想要的,也许不是,所以我希望能够做出这个选择)
    • 我希望能够将分隔符指定为字符串文字,但也接受 s和 std::string s

    这假设C++ 11。 我选择使用 标准::字符串流 因为它实现了一种标准但仍然可以自定义的方式来将某些内容转换为字符串。 欢迎发表任何意见。

    #include <iterator>
    #include <sstream>
    #include <string>
    #include <iostream> // used only in main
    #include <vector> // used only in main
    
    template< typename T >
    typename std::iterator_traits< T >::value_type
    identity(typename std::iterator_traits< T >::value_type v) {
      return v;
    }
    
    template< typename T > using IdentityType = decltype(identity< T >);
    
    template< class InItr,
              typename StrType1 = const char *,
              typename StrType2 = const char *,
              typename StrType3 = const char *,
              typename Transform = IdentityType< InItr > >
    std::string join(InItr first,
                     InItr last,
                     StrType1 &&sep = ",",
                     StrType2 &&open = "[",
                     StrType3 &&close = "]",
                     Transform tr = identity< InItr >) {
    
      std::stringstream ss;
    
      ss << std::forward< StrType2 >(open);
    
      if (first != last) {
    
        ss << tr(*first);
    
        ++first;
      }
    
      for (; first != last; ++first)
        ss << std::forward< StrType1 >(sep) << tr(*first);
    
      ss << std::forward< StrType3 >(close);
    
      return ss.str();
    }
    
    
    int main(int argc, char** argv) {
    
      const std::vector< int > vec{2, 4, 6, 8, 10};
    
      std::cout << join(vec.begin(), vec.end()) << std::endl;
      std::cout << join(vec.begin(), vec.end(), "|", "(", ")",
                        [](int v){ return v + v; }) << std::endl;
    
      const std::vector< char > vec2{2, 4, 6, 8, 10};
      std::cout << join(vec2.begin(), vec2.end()) << std::endl;
      std::cout << join(vec2.begin(), vec2.end(), "|", "(", ")",
              [](char v){ return static_cast<int>(v); }) << std::endl;
    }
    

    [2,4,6,8,10]
    (4|8|12|16|20)
    [<unprintable-char>,<unprintable-char>,<unprintable-char>,
    ]
    (2|4|6|8|10)
    
        8
  •  -1
  •   Awais Rafique    8 年前
    int array[ 6 ] = { 1, 2, 3, 4, 5, 6 };
    std::vector< int > a( array, array + 6 );
    stringstream dataString; 
    ostream_iterator<int> output_iterator(dataString, ";"); // here ";" is delimiter 
    std::copy(a.begin(), a.end(), output_iterator);
    cout<<dataString.str()<<endl;
    

    输出=1;2;三;4;5;6;

        9
  •  -2
  •   max.kondr    8 年前

    更快的变型:

    vector<string> x = {"1", "2", "3"};
    string res;
    res.reserve(16);
    
    std::accumulate(std::begin(x), std::end(x), 0,
                    [&res](int &, string &s)
                    {
                        if (!res.empty())
                        {
                            res.append(",");
                        }
                        res.append(s);
                        return 0;
                   });
    

    它不创建临时字符串,只为整个字符串结果分配一次内存,并将每个元素附加到&结尾;物件