Как объединить несколько строк C++ в одной строке?
C# имеет синтаксическую функцию, где вы можете объединить много типов данных вместе на 1 строке.
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.";
приведенный выше код выдает ошибку.
22 ответов:
#include <sstream> #include <string> std::stringstream ss; ss << "Hello, world, " << myInt << niceToSeeYouString; std::string s = ss.str();взгляните на эту статью гуру недели от Херба Саттера:строка форматирования из фермерской усадьбы
s += "Hello world, " + "nice to see you, " + "or not.";эти литералы массива символов не являются c++ std::strings-вы ned, чтобы преобразовать их:
s += string("Hello world, ") + string("nice to see you, ") + string("or not.");для преобразования ints (или любого другого streamable типа) вы можете использовать boost lexical_cast или предоставить свою собственную функцию:
template <typename T> string Str( const T & t ) { ostringstream os; os << t; return os.str(); }теперь вы можете сказать такие вещи, как:
string s = "The meaning is " + Str( 42 );
за 5 лет никто не упомянул
.append?#include <string> std::string s; s.append("Hello world, "); s.append("nice to see you, "); s.append("or not.");
ваш код может быть записан как1,
s = "Hello world," "nice to see you," "or not."...но я сомневаюсь, что это то, что вы ищете. В вашем случае, вы, вероятно, ищете потоки:
std::stringstream ss; ss << "Hello world, " << 42 << "nice to see you."; std::string s = ss.str();
1"можно записать как" : это работает только для строковых литералов. Конкатенация выполняется компилятором.
использование C++14 пользовательских литералов и
std::to_stringкод становится проще.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;обратите внимание, что конкатенация строковых литералов может быть сделано во время компиляции. Просто удалите
+.str += "Hello World, " "nice to see you, " "or not";
чтобы предложить решение, которое является более однострочным: функция
concatможет быть реализовано для уменьшения "классического" решения на основе stringstream до один заявление. Он основан на вариативных шаблонах и идеальной переадресации.
использование:
std::string s = concat(someObject, " Hello, ", 42, " I concatenate", anyStreamableType);
реализация:
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(); }
boost:: format
или std::stringstream
std::stringstream msg; msg << "Hello world, " << myInt << niceToSeeYouString; msg.str(); // returns std::string object
The актуальная проблема это конкатенация строковых литералов с
+сбой в C++:
string s;s += "Hello world, " + "nice to see you, " + "or not.";
Приведенный выше код выдает ошибку.в C++ (также В C) вы объединяете строковые литералы, просто помещая их рядом друг с другом:
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.";это имеет смысл, если вы создаете код в макросах:
#define TRACE(arg) cout << #arg ":" << (arg) << endl;...простой макрос, который можно использовать как это
int a = 5; TRACE(a) a += 7; TRACE(a) TRACE(a+7) TRACE(17*11)(демо ...)
или, если вы настаиваете в использовании
+для строковых литералов (как уже предлагалось underscore_d):string s = string("Hello world, ")+"nice to see you, "+"or not.";другое решение сочетает в себе строку и
const char*для каждого шага конкатенацииstring s; s += "Hello world, " s += "nice to see you, " s += "or not.";
С {fmt} библиотека вы можете сделать:
auto s = fmt::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);подмножество библиотеки предлагается для стандартизации как Форматирование Текста P0645 и, если принято, то выше станет:
auto s = std::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);отказ от ответственности: я автор библиотеки {fmt}.
вы должны были бы определить оператор+ () для каждого типа данных, который вы хотели бы объединить в строку, но поскольку оператор
блин, бить на 50 секунд...
может, тебе нравится мой "стример" решение, чтобы действительно сделать это в одну строку:
#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; }
Если вы выпишете
+=, он выглядит почти так же, как C#string s("Some initial data. "); int i = 5; s = s + "Hello world, " + "nice to see you, " + to_string(i) + "\n";
как говорили другие, основная проблема с кодом 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
вы можете использовать этот заголовок для этого:https://github.com/theypsilon/concat
using namespace concat; assert(concat(1,2,3,4,5) == "12345");под капотом вы будете использовать std:: ostringstream.
если вы готовы использовать
c++11можно использовать пользовательские строковые литералы и определить два шаблона функций, которые перегружают оператор plus дляstd::string"объект" и любой другой объект. Единственная ловушка - не перегружать плюсовые операторыstd::string, в противном случае компилятор не знает, какой оператор использовать. Вы можете сделать это с помощью шаблонаstd::enable_ifСtype_traits. После этого строки ведут себя так же, как в Java или C#. Смотрите мои пример реализации для деталей.основной код
#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; }файл c_sharp_strings.ГЭС
включите этот файл заголовка во все места, где вы хотите иметь эти строки.
#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
вы также можете "расширить" класс string и выбрать оператор, который вы предпочитаете (
вот код с помощью оператора
Примечание: Если вы раскомментируете s1.reserve (30), есть только 3 новых запроса оператора (1 для s1, 1 для s2, 1 для reserve ; вы не можете зарезервировать во время конструктора, к сожалению); без резерва s1 должен запрашивать больше памяти по мере ее роста, поэтому это зависит от вашей реализации компилятора растет фактор (мой, кажется, 1.5, 5 новых вызовов() в этом примере)
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; }
что-то вроде этого работает для меня
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);
на основе вышеуказанных решений я сделал класс 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++ ?
в c11:
void printMessage(std::string&& message) { std::cout << message << std::endl; return message; }это позволит вам создать вызов функции такой:
printMessage("message number : " + std::to_string(id));напечатает: номер сообщения: 10
вот однострочное решение:
#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+чтобы убедиться, что его левый операнд всегдаstd::string. Таким образом, мы объединяемstd::stringнаconst char *операнд справа и вернуть другойstd::string, каскадирования эффект.Примечание: есть несколько вариантов для правильного операнда, в том числе
const char *,std::stringиchar.это до вас, чтобы решить, является ли магическое число 13 или 6227020800.
это работает для меня:
#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; }выход:
c:\example\myfile.txt
Comments