Как объединить несколько строк C ++ в одну строку? - PullRequest
131 голосов
/ 19 марта 2009

C # имеет синтаксическую функцию, в которой вы можете объединить множество типов данных в одну строку.

string s = new String();
s += "Hello world, " + myInt + niceToSeeYouString;
s += someChar1 + interestingDecimal + someChar2;

Что будет эквивалентом в C ++? Насколько я вижу, вам придется делать все это на отдельных строках, поскольку он не поддерживает несколько строк / переменных с оператором +. Это нормально, но выглядит не очень аккуратно.

string s;
s += "Hello world, " + "nice to see you, " + "or not.";

Приведенный выше код выдает ошибку.

Ответы [ 24 ]

3 голосов
/ 19 марта 2009

Вы должны будете определить operator + () для каждого типа данных, который вы хотите скрыть в строке, но, поскольку operator << определен для большинства типов, вы должны использовать std :: stringstream. </p>

Черт, бить на 50 секунд ...

3 голосов
/ 22 февраля 2016

Как уже говорили другие, основная проблема с кодом OP заключается в том, что оператор + не объединяет const char *; это работает с std::string, хотя.

Вот еще одно решение, которое использует лямбды C ++ 11 и for_each и позволяет предоставить separator для разделения строк:

#include <vector>
#include <algorithm>
#include <iterator>
#include <sstream>

string join(const string& separator,
            const vector<string>& strings)
{
    if (strings.empty())
        return "";

    if (strings.size() == 1)
        return strings[0];

    stringstream ss;
    ss << strings[0];

    auto aggregate = [&ss, &separator](const string& s) { ss << separator << s; };
    for_each(begin(strings) + 1, end(strings), aggregate);

    return ss.str();
}

Использование:

std::vector<std::string> strings { "a", "b", "c" };
std::string joinedStrings = join(", ", strings);

Кажется, он хорошо масштабируется (линейно), по крайней мере, после быстрого теста на моем компьютере; вот быстрый тест, который я написал:

#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <chrono>

using namespace std;

string join(const string& separator,
            const vector<string>& strings)
{
    if (strings.empty())
        return "";

    if (strings.size() == 1)
        return strings[0];

    stringstream ss;
    ss << strings[0];

    auto aggregate = [&ss, &separator](const string& s) { ss << separator << s; };
    for_each(begin(strings) + 1, end(strings), aggregate);

    return ss.str();
}

int main()
{
    const int reps = 1000;
    const string sep = ", ";
    auto generator = [](){return "abcde";};

    vector<string> strings10(10);
    generate(begin(strings10), end(strings10), generator);

    vector<string> strings100(100);
    generate(begin(strings100), end(strings100), generator);

    vector<string> strings1000(1000);
    generate(begin(strings1000), end(strings1000), generator);

    vector<string> strings10000(10000);
    generate(begin(strings10000), end(strings10000), generator);

    auto t1 = chrono::system_clock::now();
    for(int i = 0; i<reps; ++i)
    {
        join(sep, strings10);
    }

    auto t2 = chrono::system_clock::now();
    for(int i = 0; i<reps; ++i)
    {
        join(sep, strings100);
    }

    auto t3 = chrono::system_clock::now();
    for(int i = 0; i<reps; ++i)
    {
        join(sep, strings1000);
    }

    auto t4 = chrono::system_clock::now();
    for(int i = 0; i<reps; ++i)
    {
        join(sep, strings10000);
    }

    auto t5 = chrono::system_clock::now();

    auto d1 = chrono::duration_cast<chrono::milliseconds>(t2 - t1);
    auto d2 = chrono::duration_cast<chrono::milliseconds>(t3 - t2);
    auto d3 = chrono::duration_cast<chrono::milliseconds>(t4 - t3);
    auto d4 = chrono::duration_cast<chrono::milliseconds>(t5 - t4);

    cout << "join(10)   : " << d1.count() << endl;
    cout << "join(100)  : " << d2.count() << endl;
    cout << "join(1000) : " << d3.count() << endl;
    cout << "join(10000): " << d4.count() << endl;
}

Результаты (миллисекунды):

join(10)   : 2
join(100)  : 10
join(1000) : 91
join(10000): 898
3 голосов
/ 31 августа 2016

Может быть, вам нравится мое решение "Streamer", чтобы оно действительно выполнялось в одной строке:

#include <iostream>
#include <sstream>
using namespace std;

class Streamer // class for one line string generation
{
public:

    Streamer& clear() // clear content
    {
        ss.str(""); // set to empty string
        ss.clear(); // clear error flags
        return *this;
    }

    template <typename T>
    friend Streamer& operator<<(Streamer& streamer,T str); // add to streamer

    string str() // get current string
    { return ss.str();}

private:
    stringstream ss;
};

template <typename T>
Streamer& operator<<(Streamer& streamer,T str)
{ streamer.ss<<str;return streamer;}

Streamer streamer; // make this a global variable


class MyTestClass // just a test class
{
public:
    MyTestClass() : data(0.12345){}
    friend ostream& operator<<(ostream& os,const MyTestClass& myClass);
private:
    double data;
};

ostream& operator<<(ostream& os,const MyTestClass& myClass) // print test class
{ return os<<myClass.data;}


int main()
{
    int i=0;
    string s1=(streamer.clear()<<"foo"<<"bar"<<"test").str();                      // test strings
    string s2=(streamer.clear()<<"i:"<<i++<<" "<<i++<<" "<<i++<<" "<<0.666).str(); // test numbers
    string s3=(streamer.clear()<<"test class:"<<MyTestClass()).str();              // test with test class
    cout<<"s1: '"<<s1<<"'"<<endl;
    cout<<"s2: '"<<s2<<"'"<<endl;
    cout<<"s3: '"<<s3<<"'"<<endl;
}
2 голосов
/ 10 сентября 2015

Если вы напишите +=, он будет выглядеть почти так же, как C #

string s("Some initial data. "); int i = 5;
s = s + "Hello world, " + "nice to see you, " + to_string(i) + "\n";
1 голос
/ 01 октября 2018

Вот однострочное решение:

#include <iostream>
#include <string>

int main() {
  std::string s = std::string("Hi") + " there" + " friends";
  std::cout << s << std::endl;

  std::string r = std::string("Magic number: ") + std::to_string(13) + "!";
  std::cout << r << std::endl;

  return 0;
}

Хотя это немного некрасиво, я думаю, что оно примерно такое же чистое, как у кошки в C ++.

Мы приводим первый аргумент к std::string и затем используем (слева направо) порядок вычисления operator+, чтобы гарантировать, что его операнд left всегда равен std::string. Таким образом, мы объединяем std::string слева с операндом const char * справа и возвращаем еще один std::string, каскадный эффект.

Примечание: есть несколько опций для правого операнда, включая const char *, std::string и char.

Вам решать, будет ли магическое число 13 или 6227020800.

1 голос
/ 11 ноября 2017

В с11:

void printMessage(std::string&& message) {
    std::cout << message << std::endl;
    return message;
}

это позволит вам создать вызов функции следующим образом:

printMessage("message number : " + std::to_string(id));

напечатает: номер сообщения: 10

1 голос
/ 02 ноября 2017

Основываясь на вышеуказанных решениях, я создал класс var_string для своего проекта, чтобы облегчить жизнь. Примеры:

var_string x("abc %d %s", 123, "def");
std::string y = (std::string)x;
const char *z = x.c_str();

Сам класс:

#include <stdlib.h>
#include <stdarg.h>

class var_string
{
public:
    var_string(const char *cmd, ...)
    {
        va_list args;
        va_start(args, cmd);
        vsnprintf(buffer, sizeof(buffer) - 1, cmd, args);
    }

    ~var_string() {}

    operator std::string()
    {
        return std::string(buffer);
    }

    operator char*()
    {
        return buffer;
    }

    const char *c_str()
    {
        return buffer;
    }

    int system()
    {
        return ::system(buffer);
    }
private:
    char buffer[4096];
};

Все еще задаетесь вопросом, будет ли что-то лучше в C ++?

1 голос
/ 15 октября 2017

Как-то так у меня работает

namespace detail {
    void concat_impl(std::ostream&) { /* do nothing */ }

    template<typename T, typename ...Args>
    void concat_impl(std::ostream& os, const T& t, Args&&... args)
    {
        os << t;
        concat_impl(os, std::forward<Args>(args)...);
    }
} /* namespace detail */

template<typename ...Args>
std::string concat(Args&&... args)
{
    std::ostringstream os;
    detail::concat_impl(os, std::forward<Args>(args)...);
    return os.str();
}
// ...
std::string s{"Hello World, "};
s = concat(s, myInt, niceToSeeYouString, myChar, myFoo);
1 голос
/ 23 июля 2014

Вы можете использовать этот заголовок для этого: https://github.com/theypsilon/concat

using namespace concat;

assert(concat(1,2,3,4,5) == "12345");

Под капотом вы будете использовать std :: ostringstream.

1 голос
/ 10 марта 2016

Вы также можете «расширить» класс строки и выбрать предпочитаемый оператор (<<, &, | и т. Д.) </p>

Вот код, использующий оператор <<, чтобы показать, что нет конфликта с потоками </p>

примечание: если вы раскомментируете s1.reserve (30), есть только 3 новых запроса оператора () (1 для s1, 1 для s2, 1 для резерва; к сожалению, вы не можете зарезервировать во время конструктора); без резерва, s1 должен запрашивать больше памяти по мере роста, так что это зависит от фактора роста реализации вашего компилятора (в моем примере это 1,5, 5 вызовов new ())

namespace perso {
class string:public std::string {
public:
    string(): std::string(){}

    template<typename T>
    string(const T v): std::string(v) {}

    template<typename T>
    string& operator<<(const T s){
        *this+=s;
        return *this;
    }
};
}

using namespace std;

int main()
{
    using string = perso::string;
    string s1, s2="she";
    //s1.reserve(30);
    s1 << "no " << "sunshine when " << s2 << '\'' << 's' << " gone";
    cout << "Aint't "<< s1 << " ..." <<  endl;

    return 0;
}
...