2012-02-14 122 views
16

我想用自定义分隔符将vector的内容复制到一个长string。到目前为止,我已经试过:std ::向带自定义分隔符的字符串向量

// .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 

然后我试图使用charostream_iterator<int>(s,&delimiter) 但我在字符串中出现奇怪的字符。

任何人都可以帮助我理解编译器在这里期待什么吗?

+0

是的,如果编译器友好地告诉你它期望的是什么类型的话,这不是很好。顺便说一句,你会在最后一个元素之后得到一个逗号。 – CashCow 2012-02-14 13:54:32

+0

最优雅的方法是使用boost :: algorithm :: join(),如http://stackoverflow.com/a/6334153/2056686 – StefanQ 2016-11-23 12:55:05

回答

19

Use delimiter.c_str() as the delimiter

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

这样的话,你会得到一个const char*指向字符串,这是ostream_operatorstd::string期待。

+15

+1中所述,但是请注意,这会写一个尾随分隔符'在输出中。 – 2012-02-14 13:49:43

+1

我不确定这里的表现。 'stringstream'管理自己的缓冲区,所以它必须动态增长。在生成字符串之前你知道它将会是什么长度,所以你应该在连接之前保留缓冲区。 – wilhelmtell 2012-02-14 13:56:42

+1

“你在生成字符串之前知道它将会是什么长度” - 或者无论如何你知道一个上限。如果stringstream以指数形式增长其缓冲区,那么我不会担心性能,但我不知道是否是这种情况。 – 2012-02-14 14:33:59

6
std::string Gesture::getLabeledPointsString(const std::string delimiter) { 
    return boost::join(getLabeledPoints(), delimiter); 
} 

我并不信服在这一点上introducting getLabeledPointsString

+1

ehehehheheh有没有提升的cpp? +1提升,谢谢! – nkint 2012-02-14 19:08:10

+0

@nkint:哦,你当然可以编程没有提升。但是,如果没有它的库,Python就像Python一样困难:你只需要自己创建所有的工具;) – 2012-02-14 19:37:19

8

另一种方式来做到这一点:

#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系列为基础的循环和“汽车'虽然)

7

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; 
           }); 
+1

看起来很整洁,但是这不会在过程中创建很多字符串吗?任何方式来改善这使用字符串流? – oferei 2016-03-31 07:50:24

+0

不,sttringsream更慢。 事实上,如果你只有字符串的容器,最好是编写普通的for-loop,比如 'code' string result; result.reserve(128); for(auto&it:x) if(!result.emty()) { result.append(“,”); } result.append(it); } 'code' – 2016-04-01 21:28:59

+0

听起来很合理,但这不是上面的代码。这不是追加 - 它正在产生新的字符串。 – oferei 2016-04-03 10:00:04

-1

更快变体:

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; 
       }); 

它不创建临时字符串,但只是针对整个字符串结果分配内存一次并追加各ELEM到的& RES

+0

您正在使用'accumulate()',但完全忽略该结果。你应该使用'std :: for_each()'来代替。 – 2016-07-12 23:07:59

+0

这个函数调用的实际返回值是0(参见lambda - > return 0;)。 但'res'包含结果字符串 – 2016-07-15 06:13:29

+0

您知道'accumulate'预计其功能没有副作用吗?这段代码最好用'for_each'表示。 – xtofl 2016-09-13 11:52:36

2
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; 3; 4; 5; 6;

+1

通常情况下,如果答案包括解释代码的意图以及为什么解决问题而不介绍其他问题,则答案会更有帮助。 – 2016-04-24 17:01:42

+0

它的工作!简短而甜美。谢谢。 – Amjay 2016-12-06 10:00:09

3

这是上面提供的两个答案的扩展,因为运行时间性能似乎是评论中的一个主题。我会将它添加为注释,但我还没有该特权。

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

使用字符串流:

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; 

发布版本运行时性能很接近有一些微妙之处。

累积实施速度稍微快一点(20-50ms,在1000次迭代中整个运行时间(〜180ms)的约10-30%)。但是,accumulate实现只有在lambda函数的a参数通过引用传递时才会更快。按值传递a参数导致类似的运行时差异有利于stringstream实现。当结果字符串直接返回而不是分配给立即返回的局部变量时,accumulate实现也得到了改进。 YMMV与其他C++编译器。

调试版本使用accumulate慢了5-10倍,所以我认为上面几个注释中提到的额外字符串创建由优化器解决。

我正在看一个具体的实施,使用uint8_t值的vector。完整的测试代码如下:

#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; 
} 
+0

我爱你提供的表演数字 - 非常有用的做出决定的方式或其他,谢谢! – cyberbisson 2017-09-28 14:35:35

1
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; 
} 

的几点:

  • 你并不需要一个额外的条件,以避免额外的尾随分隔符
  • 确保你不死机当向量为空时
  • 不会制作一堆临时对象(例如,不要这样做:x = x + d + y)