sintaxis programacion funciones ejemplos dev comandos codigos cadenas basicos arreglo c++ string compiler-errors concatenation

funciones - programacion c++



¿Cómo concateno múltiples cadenas de C++ en una línea? (21)

Algo como esto me funciona

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

C # tiene una característica de sintaxis donde puede concatenar muchos tipos de datos juntos en 1 línea.

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

¿Cuál sería el equivalente en C ++? Por lo que puedo ver, tendrías que hacerlo todo en líneas separadas, ya que no admite múltiples cadenas / variables con el operador +. Esto está bien, pero no se ve tan limpio.

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

El código anterior produce un error.


Basado en las soluciones anteriores, hice una clase var_string para mi proyecto para facilitar la vida. Ejemplos:

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

La clase en sí:

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

¿Aún me pregunto si habrá algo mejor en C ++?


Como otros dijeron, el principal problema con el código OP es que el operador + no concatenará const char * ; Sin embargo, funciona con std::string .

Aquí hay otra solución que usa C ++ 11 lambdas y for_each y permite proporcionar un separator para separar las cadenas:

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

Uso:

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

Parece escalar bien (linealmente), al menos después de una prueba rápida en mi computadora; he aquí una prueba rápida que he escrito:

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

Resultados (milisegundos):

join(10) : 2 join(100) : 10 join(1000) : 91 join(10000): 898


Con la biblioteca {fmt} puedes hacer:

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

Se propone un subconjunto de la biblioteca para la estandarización como formato de texto P0645 y, si se acepta, lo anterior se convertirá en:

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

Descargo de responsabilidad : soy el autor de la biblioteca {fmt}.


Debería definir operator + () para cada tipo de datos que quiera concatenar a la cadena, pero como el operador << está definido para la mayoría de los tipos, debe usar std :: stringstream.

Maldición, vencer por 50 segundos ...


El problema real era que concatenar literales de cadena con + falla en C ++:

string s;
s += "Hello world, " + "nice to see you, " + "or not.";
El código anterior produce un error.

En C ++ (también en C), concatena literales de cadena simplemente colocándolos uno al lado del otro:

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.";

Esto tiene sentido si genera código en macros:

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

... una macro simple que se puede usar así

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

( demostración en vivo ... )

o, si insistes en usar + para cadenas literales (como ya lo sugirió underscore_d ):

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

Otra solución combina una cadena y un const char* para cada paso de concatenación

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


En 5 años, nadie ha mencionado .append?

#include <string> std::string s; s.append("Hello world, "); s.append("nice to see you, "); s.append("or not.");


En c11:

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

esto te permite crear llamadas a funciones como esta:

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

se imprimirá: número de mensaje: 10


Esto funciona para mí:

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

Salida:

c:/example/myfile.txt


Para ofrecer una solución que sea más una línea-ish: se puede implementar una función concat para reducir la solución "clásica" basada en cadenas de caracteres a una única declaración . Se basa en plantillas variadic y reenvío perfecto.

Uso:

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

Implementación:

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


Puede usar este encabezado para este respecto: https://github.com/theypsilon/concat

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

Debajo del capó usarás un std :: ostringstream.


Si escribe += , parece casi lo mismo que C #

string s("Some initial data. "); int i = 5; s = s + "Hello world, " + "nice to see you, " + to_string(i) + "/n";


Si está dispuesto a usar c++11 , puede utilizar literales de cadena definidos por el usuario y definir dos plantillas de función que sobrecarguen el operador más para un objeto std::string y cualquier otro objeto. El único inconveniente es no sobrecargar a los operadores plus de std::string , de lo contrario, el compilador no sabe qué operador usar. Puedes hacer esto usando la plantilla std::enable_if de type_traits . Después de eso, las cadenas se comportan como en Java o C #. Ver mi implementación de ejemplo para más detalles.

Código 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; }

Archivo c_sharp_strings.hpp

Incluya este archivo de encabezado en todos los lugares donde desee tener estas cadenas.

#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


Tal vez le gustaría que mi solución "Streamer" realmente lo haga en una sola línea:

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


Tu código puede escribirse como 1 ,

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

... pero dudo que sea eso lo que estás buscando. En su caso, probablemente esté buscando transmisiones:

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

1 " se puede escribir como ": Esto solo funciona para literales de cadena. La concatenación es hecha por el compilador.


Usando C ++ 14 literales definidos por el usuario y std::to_string el código se vuelve más fácil.

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;

Tenga en cuenta que los literales de cadena concatenados se pueden realizar en tiempo de compilación. Solo elimina el + .

str += "Hello World, " "nice to see you, " "or not";


boost :: formato

o std :: stringstream

std::stringstream msg; msg << "Hello world, " << myInt << niceToSeeYouString; msg.str(); // returns std::string object


también puede "extender" la clase de cadena y elegir el operador que prefiera (<<, &, |, etc ...)

Aquí está el código que usa el operador << para mostrar que no hay conflicto con las transmisiones

nota: si descomenta s1.reserve (30), solo hay 3 nuevas () solicitudes de operador (1 para s1, 1 para s2, 1 para reserva, no puede reservar desafortunadamente en tiempo de constructor); sin reservas, s1 tiene que solicitar más memoria a medida que crece, por lo que depende del factor de crecimiento de la implementación del compilador (el mío parece ser 1.5, 5 llamadas nuevas () en este ejemplo)

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


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

Eche un vistazo a este artículo de Guru Of The Week de Herb Sutter: The String Formatters of Manor Farm


auto s = string("one").append("two").append("three")


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

Esos literales de matriz de caracteres no son C ++ std :: strings, es necesario convertirlos:

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

Para convertir ints (o cualquier otro tipo de transmisible) puede usar un boost lexical_cast o proporcionar su propia función:

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

Ahora puede decir cosas como:

string s = "The meaning is " + Str( 42 );