Comment concaténer plusieurs chaînes C++ sur une ligne?

C# a une caractéristique de syntaxe où vous pouvez concaténer de nombreux types de données ensemble sur une ligne.

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

Quel serait l'équivalent en C++? Pour autant que je puisse voir, vous devez le faire sur des lignes séparées car il ne supporte pas plusieurs chaînes/variables avec l'opérateur+. C'est OK, mais ça n'a pas l'air aussi net.

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

Le code ci-dessus produit une erreur.

116
demandé sur Wolf 2009-03-19 19:23:11

22 réponses

#include <sstream>
#include <string>

std::stringstream ss;
ss << "Hello, world, " << myInt << niceToSeeYouString;
std::string s = ss.str();

regardez cet article du gourou de la semaine de Herb Sutter: the String Formatters of Manor Farm

193
répondu Paolo Tedesco 2009-03-19 16:27:20
s += "Hello world, " + "nice to see you, " + "or not.";

ces caractères littéraux ne sont pas c++ std:: strings-vous ned pour les convertir:

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

pour convertir les ints (ou tout autre type de flux), vous pouvez utiliser un boost lexical_cast ou fournir votre propre fonction:

template <typename T>
string Str( const T & t ) {
   ostringstream os;
   os << t;
   return os.str();
}

vous pouvez maintenant dire des choses comme:

string s = "The meaning is " + Str( 42 );
46
répondu 2009-03-19 16:26:23

en 5 ans personne n'a mentionné .append ?

#include <string>

std::string s;
s.append("Hello world, ");
s.append("nice to see you, ");
s.append("or not.");
42
répondu Michel 2018-06-25 12:04:34

votre code peut s'écrire comme 1 ,

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

...mais je doute que c'est ce que vous cherchez. Dans votre cas, vous êtes probablement à la recherche de flux:

std::stringstream ss;
ss << "Hello world, " << 42 << "nice to see you.";
std::string s = ss.str();

1 " peut être écrit comme ": cela ne fonctionne que pour les cordes littérales. La concaténation est faite par le compilateur.

34
répondu John Dibling 2013-09-06 17:37:24

utilisant C++14 défini par l'utilisateur littérales et std::to_string le code devient plus facile.

using namespace std::literals::string_literals;
std::string str;
str += "Hello World, "s + "nice to see you, "s + "or not"s;
str += "Hello World, "s + std::to_string(my_int) + other_string;

notez qu'il est possible de concaténer la littérature à cordes au moment de la compilation. Il suffit de supprimer le + .

str += "Hello World, " "nice to see you, " "or not";
20
répondu Rapptz 2014-07-23 06:48:17

pour offrir une solution plus monoligne: une fonction concat peut être implémentée pour réduire la solution" classique "basée sur stringstream à une déclaration unique . Il est basé sur des modèles variadiques et Perfect forwarding.


Utilisation:

std::string s = concat(someObject, " Hello, ", 42, " I concatenate", anyStreamableType);

mise en œuvre:

void addToStream(std::ostringstream&)
{
}

template<typename T, typename... Args>
void addToStream(std::ostringstream& a_stream, T&& a_value, Args&&... a_args)
{
    a_stream << std::forward<T>(a_value);
    addToStream(a_stream, std::forward<Args>(a_args)...);
}

template<typename... Args>
std::string concat(Args&&... a_args)
{
    std::ostringstream s;
    addToStream(s, std::forward<Args>(a_args)...);
    return s.str();
}
13
répondu SebastianK 2015-08-04 09:55:39

boost:: format

ou std:: stringstream

std::stringstream msg;
msg << "Hello world, " << myInt  << niceToSeeYouString;
msg.str(); // returns std::string object
7
répondu bayda 2013-03-22 07:55:09

Le problème , c'est que la concaténation de chaînes de caractères littérales avec + échoue en C++:

string s;

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

Le code ci-dessus produit une erreur.

en C++ (aussi en C), vous concaténez des chaînes de caractères en les plaçant juste à côté de l'autre:

string s0 = "Hello world, " "nice to see you, " "or not.";
string s1 = "Hello world, " /*same*/ "nice to see you, " /*result*/ "or not.";
string s2 = 
    "Hello world, " /*line breaks in source code as well as*/ 
    "nice to see you, " /*comments don't matter*/ 
    "or not.";

ce qui fait sens, si vous générez du code dans les macros:

#define TRACE(arg) cout << #arg ":" << (arg) << endl;

...une macro simple qui peut être utilisée comme ceci

int a = 5;
TRACE(a)
a += 7;
TRACE(a)
TRACE(a+7)
TRACE(17*11)

( live demo ... )

ou, si vous insistez pour utiliser le + pour les chaînes de caractères (comme déjà suggéré par sous-titre_d ):

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

une autre solution combine une corde et un const char* pour chaque étape de concaténation

string s;
s += "Hello world, "
s += "nice to see you, "
s += "or not.";
4
répondu Wolf 2017-09-19 09:35:11

Avec la {fmt} bibliothèque vous pouvez le faire:

auto s = fmt::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);

il est proposé de normaliser un sous-ensemble de la bibliothèque sous le nom de P0645 formatage du texte et, si accepté, ce qui précède deviendra:

auto s = std::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);

clause de non-responsabilité : je suis l'auteur de la bibliothèque {fmt}.

3
répondu vitaut 2018-04-29 17:25:54

vous devez définir operator+() pour chaque type de données que vous voulez concentrer à la chaîne, mais comme operator<< est défini pour la plupart des types, vous devez utiliser std::stringstream.

Merde, battu de 50 secondes...

2
répondu tstenner 2009-03-19 16:28:09

peut-être que vous aimez ma solution "Streamer" pour vraiment le faire dans une ligne:

#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
répondu bterwijn 2016-08-31 15:57:19

si vous écrivez le += , il semble presque le même que C#

string s("Some initial data. "); int i = 5;
s = s + "Hello world, " + "nice to see you, " + to_string(i) + "\n";
1
répondu Eponymous 2015-09-10 16:47:26

comme d'autres l'ont dit, le principal problème avec le code OP est que l'opérateur + ne concaténate pas const char * ; il fonctionne avec std::string , cependant.

Voici une autre solution qui utilise C++11 lambdas et for_each et permet de fournir un separator pour séparer les cordes:

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

Utilisation:

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

il semble bien dimensionner (linéairement), au moins après un test rapide sur mon ordinateur; voici un test rapide que j'ai écrit:

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

Results (millisecondes):

join(10)   : 2
join(100)  : 10
join(1000) : 91
join(10000): 898
1
répondu elnigno 2016-02-22 16:21:27

vous pouvez utiliser cet en-tête à cet égard: https://github.com/theypsilon/concat

using namespace concat;

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

sous le capot vous utiliserez un std:: ostringstream.

0
répondu José Manuel 2014-07-23 07:21:46

si vous êtes prêt à utiliser c++11 vous pouvez utiliser littérales à chaîne définies par l'utilisateur et définir deux modèles de fonction qui surchargent l'opérateur plus pour un objet std::string et tout autre objet. Le seul piège est de ne pas surcharger les opérateurs plus de std::string , sinon le compilateur ne sait pas quel opérateur utiliser. Vous pouvez le faire en utilisant le modèle std::enable_if de type_traits . Après que les cordes se comportent comme dans Java ou C#. Voir mon exemple implémentation pour plus de détails.

code Principal

#include <iostream>
#include "c_sharp_strings.hpp"

using namespace std;

int main()
{
    int i = 0;
    float f = 0.4;
    double d = 1.3e-2;
    string s;
    s += "Hello world, "_ + "nice to see you. "_ + i
            + " "_ + 47 + " "_ + f + ',' + d;
    cout << s << endl;
    return 0;
}

fichier c_sharp_strings.HPP

incluez ce fichier d'en-tête dans tous les endroits où vous voulez avoir ces chaînes.

#ifndef C_SHARP_STRING_H_INCLUDED
#define C_SHARP_STRING_H_INCLUDED

#include <type_traits>
#include <string>

inline std::string operator "" _(const char a[], long unsigned int i)
{
    return std::string(a);
}

template<typename T> inline
typename std::enable_if<!std::is_same<std::string, T>::value &&
                        !std::is_same<char, T>::value &&
                        !std::is_same<const char*, T>::value, std::string>::type
operator+ (std::string s, T i)
{
    return s + std::to_string(i);
}

template<typename T> inline
typename std::enable_if<!std::is_same<std::string, T>::value &&
                        !std::is_same<char, T>::value &&
                        !std::is_same<const char*, T>::value, std::string>::type
operator+ (T i, std::string s)
{
    return std::to_string(i) + s;
}

#endif // C_SHARP_STRING_H_INCLUDED
0
répondu Scindix 2016-01-02 21:34:25

vous pouvez également "étendre" la classe string et choisir l'opérateur que vous préférez ( <<, &, |, etc...)

voici le code utilisant l'opérateur < < pour montrer qu'il n'y a pas de conflit avec les flux

note: si vous décommentez s1.reserve (30), il n'y a que 3 nouvelles requêtes d'opérateur () (1 pour s1, 1 pour s2, 1 pour reserve ; vous ne pouvez pas réserver au moment du constructeur malheureusement); sans reserve, s1 doit demander plus de mémoire au fur et à mesure de sa croissance, donc cela dépend de votre compilateur de mise en œuvre de croître facteur (le mien semble être de 1,5, 5 nouveau() appelle dans cet exemple)

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;
}
0
répondu ddy 2016-03-10 15:23:17
auto s = string("one").append("two").append("three")
0
répondu ShitalShah 2017-10-07 03:58:05

quelque Chose comme cela fonctionne pour moi

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);
0
répondu smoothware 2017-10-15 09:24:29

basé sur les solutions ci-dessus j'ai fait une classe var_string pour mon projet de rendre la vie facile. Exemples:

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

la classe elle-même:

#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];
};

se demande toujours s'il y aura quelque chose de mieux en C++ ?

0
répondu Bart Mensfort 2017-11-02 10:39:30

en c11:

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

cela vous permet de créer l'appel de fonction comme ceci:

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

affichera: message number: 10

0
répondu devcodexyz 2017-11-11 07:11:27

Voici la solution à une doublure:

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

même si c'est un tout petit peu moche, je pense que c'est à peu près aussi propre que vous Chat obtenir en C++.

nous présentons le premier argument à un std::string et nous utilisons ensuite l'ordre d'évaluation (de gauche à droite) de operator+ pour nous assurer que son gauche opérande est toujours un std::string . De cette façon, nous concaténons le std::string sur la gauche avec le const char * opérande sur la droite et retourner un autre std::string , cascade de l'effet.

Note: il y a quelques options pour l'opérande de droite, y compris const char * , std::string , et char .

c'est à vous de décider si le nombre magique est 13 ou 6227020800.

0
répondu Apollys 2018-10-01 18:21:45

ça marche pour moi:

#include <iostream>

using namespace std;

#define CONCAT2(a,b)     string(a)+string(b)
#define CONCAT3(a,b,c)   string(a)+string(b)+string(c)
#define CONCAT4(a,b,c,d) string(a)+string(b)+string(c)+string(d)

#define HOMEDIR "c:\example"

int main()
{

    const char* filename = "myfile";

    string path = CONCAT4(HOMEDIR,"\",filename,".txt");

    cout << path;
    return 0;
}

sortie:

c:\example\myfile.txt
-1
répondu erik80 2013-09-25 17:36:43