std :: vector в строку с пользовательским разделителем - PullRequest
24 голосов
/ 14 февраля 2012

Я хотел бы скопировать содержимое 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) но в строке появляются странные символы.

Может ли кто-нибудь помочь мне понять, что здесь ожидает компилятор?

Ответы [ 8 ]

21 голосов
/ 14 февраля 2012

Используйте delimiter.c_str() в качестве разделителя :

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

Таким образом, вы получаете const char*, указывающий на строку, а это то, что ostream_operator ожидает от вашего std::string.

11 голосов
/ 17 февраля 2016

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;
                                });
9 голосов
/ 14 февраля 2012

Еще один способ сделать это:

#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 основан на диапазоне для цикла и 'auto')

9 голосов
/ 14 февраля 2012
std::string Gesture::getLabeledPointsString(const std::string delimiter) {
  return boost::join(getLabeledPoints(), delimiter);
}

Я не настолько убежден в том, чтобы ввести getLabeledPointsString на этом этапе;)

7 голосов
/ 15 октября 2016

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

Я протестировал 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 ++.

Сборка отладки была в 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;
}
2 голосов
/ 09 ноября 2016
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)
0 голосов
/ 23 апреля 2016
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;

0 голосов
/ 02 апреля 2016

более быстрый вариант:

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...