2012-02-14 2 views
16

Я хотел бы скопировать содержимое vector в один длинный string с пользовательским разделителем. До сих пор я пытался:std :: vector to string with custom delimiter

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

Да, было бы неплохо, если бы компилятор любезно сказал вам, какого типа он ожидал. Кстати, вы получите запятую после своего последнего элемента. – CashCow

+0

Самый элегантный способ - использовать boost :: algorithm :: join() для этого, как описано в http://stackoverflow.com/a/6334153/2056686 – StefanQ

ответ

19

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.

+15

+1, но также обратите внимание, что это будет писать трейлинг-разделитель 'на выходе. –

+1

Я не уверен в производительности здесь. «Строковый поток» управляет собственным буфером, поэтому он должен динамически расти. Здесь вы знаете, прежде чем генерировать строку, какой длины она будет, поэтому вы должны зарезервировать буфер перед конкатенацией. – wilhelmtell

+1

«вы знаете, прежде чем генерировать строку, какую длину она будет» - или, во всяком случае, вы знаете верхнюю границу. Если stringstream увеличивает свой буфер экспоненциально, то я бы не стал беспокоиться о производительности, но я не знаю, так ли это. –

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

Я не то, что убежден Introducting getLabeledPointsString в этой точке;)

+1

ehehehheheh есть cpp без boost? +1 для повышения, спасибо! – nkint

+0

@nkint: О, конечно, вы можете запрограммировать без повышения. Но это примерно так же сложно, как Python без его библиотек: вам просто нужно создать все инструменты самостоятельно;) –

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

+0

нет, sttringsream медленнее даже больше. Фактически, если у вас есть только контейнер строк, лучше написать обычный для цикла, например 'code' string result; result.reserve (128); для (авто & it: x) { если (! Result.emty()) { результат.append (","); } result.append (it); } 'code' –

+0

Звучит разумно, но это не то, что делает код выше. Это не добавление - это генерирование новых строк. – oferei

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

не создает промежуточные строки, а просто выделить память один раз для всей строки результата и добавляет каждый эль до конца & Реза

+0

Вы используете 'accumulate()', но полностью игнорируете этот результат. Вместо этого вы должны использовать 'std :: for_each()'. –

+0

Фактически возвращаемое значение этого вызова функции равно 0 (см. Lambda -> return 0;). но 'res' содержит строку результата –

+0

Знаете ли вы, что 'accumulate' ожидает, что его функция не будет иметь побочных эффектов?Этот код лучше выражать как 'for_each'. – xtofl

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

Как правило, ответы гораздо полезнее, если они содержат объяснение того, что должен делать код, и почему это решает проблему, не представляя других. –

+0

Это сработало !. короткий и сладкий. Спасибо. – Amjay

3

Это расширение для двух ответов, уже представленных выше, поскольку производительность во время исполнения показалась темой в комментариях. Я бы добавил его в качестве комментариев, но у меня пока нет этой привилегии.

Я тестировал 2 реализаций для выполнения время выполнения с помощью 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; 

выпуска производительность сборки во время выполнения был близок с несколькими тонкостями.

Реализация накопления была немного быстрее (20-50 мс, ~ 10-30% от общего времени выполнения (~ 180 мс) на 1000 итераций по 256-элементному вектору).Однако реализация accumulate была только быстрее, когда параметр a для функции лямбда был передан по ссылке. Передача параметра a по значению привела к аналогичной разнице во времени выполнения в пользу реализации stringstream. Реализация accumulate также улучшила некоторые, когда возвращаемая строка результата была возвращена напрямую, а не назначена локальной переменной, которая была немедленно возвращена. YMMV с другими компиляторами C++.

Сборка Debug была в 5-10 раз медленнее, используя accumulate, поэтому я думаю, что добавленное строковое создание, указанное в нескольких комментариях выше, разрешено оптимизатором.

Я искал конкретную реализацию, используя 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; 
} 
+0

Мне нравится, что вы предоставили номера производительности - очень полезно для принятия решения так или иначе, спасибо! – cyberbisson

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)
Смежные вопросы