Related
If using g++ and clang++, I get ++my string==my string##my string--. While MSVC and Intel Compiler, it is ++==my string##my string--.
Why?
#include <string>
#include <iostream>
using namespace std;
string test()
{
string s0 = "my string";
return s0;
}
int main()
{
string s = test();
const char* s1 = test().c_str();
const char* s2 = s.c_str();
cout << "++" << s1 << "==" << s2 << "##" << test().c_str() << "--" << endl;
return 0;
}
Is it an undefined behavior?
In a comment, you asked:
Why test().c_str() can work but s1 not?
test().c_str() works only in some contexts, not all contexts.
std::cout << test().c_str() << std::endl;
is guaranteed to work since the temporary returned by test() is required to stay alive until the execution of the statement is complete.
On the other hand,
char const* s1 = test().c_str();
std:cout << s1 << std::endl;
is undefined behavior since the temporary is not required to live beyond completion of execution of the first line.
This question already has answers here:
How do you construct a std::string with an embedded null?
(11 answers)
Closed 6 years ago.
I am writing a unit test where a string has to end in a binary zero.
Consider const char * data_c = "1234"; , here data_c contains 5 characters including a zero. std::string removes that zero and tracks the size like a vector. std::string data_cpp = "1234";
The string I need to create has a binary zero at the end. Initialising std::string with simple means seems problematic. std::string data_cpp{"ABC\0"}; Gives back a string of size 3;
The following minimal example show passing and not passing examples to illustrate my problem further:
#include <iostream>
#include <string>
void testString(std::string name, std::string str)
{
int e = 0;
std::cout << name << "\n";
std::cout << "----------------------" << "\n";
if (4 != str.size())
{
std::cout << "Size was not 4" << "\n";
e += 1;
}
char testvals[] = {'A', 'B', 'C', '\0'};
for (size_t n = 0; n < 4 && n < str.size(); ++n)
{
if (str[n] != testvals[n])
{
std::cout << "Character " << std::to_string(n) << " '" << str[n] << "'" << " did not match" << "\n";
e += 1;
}
}
std::cout << "ERRORS: " << std::to_string(e) << "\n";
std::cout << "----------------------" << std::endl;
}
template<size_t N>
std::string CutomInitString(const char(&str)[N])
{
return std::string{str, str + N - 1};
}
int main()
{
std::string one{"ABC\0"};
testString("ONE", one); //FAILS
const char two_c[] = "ABC\0";
std::string two{two_c};
testString("TWO", two); //FAILS
const char three_c[] = "ABC\0";
std::string three{three_c, three_c + (sizeof(three_c) / sizeof(char)) - 1};
testString("THREE", three); //PASS, also ugly
const char four_c[] = "ABC\0";
std::string four{CutomInitString(four_c)};
testString("FOUR", four); //PASS, also ugly
}
An example for simple would be std::string one.
Is there a simple form that I can use?
You could use an std::string constructor which takes the size of the buffer:
basic_string( const CharT* s,
size_type count,
const Allocator& alloc = Allocator() );
Edit: Rewrote answer after some consideration.
The real problem here is not in std::string but actually in the built-in array type, which doesn't really work like a container. The solution below isn't much different from yours, but you might think it's less ugly if you use to_array to convert the built-in array type to std::array immediately.
auto my_array = std::experimental::to_array("ABC\0");
std::string my_string{my_array.begin(), my_array.end()};
In Pascal Lazarus/Delphi, we have a function QuotedStr() that wraps any string within single quotes.
Here's an example of my current C++ code:
//I need to quote tblCustomers
pqxx::result r = txn.exec( "Select * from \"tblCustomers\" ");
Another one:
//I need to quote cCustomerName
std::cout << "Name: " << r[a]["\"cCustomerName\""];
Similar to the above, I have to frequently double-quote strings. Typing this in is kind of slowing me down. Is there a standard function I can use for this?
BTW, I develop using Ubuntu/Windows with Code::Blocks. The technique used must be compatible across both platforms. If there's no function, this means that I must write one.
C++14 added std::quoted which does exactly that, and more actually: it takes care of escaping quotes and backslashes in output streams, and of unescaping them in input streams. It is efficient, in that it does not create a new string, it's really a IO manipulator. (So you don't get a string, as you'd like.)
#include <iostream>
#include <iomanip>
#include <sstream>
int main()
{
std::string in = "\\Hello \"Wörld\"\\\n";
std::stringstream ss;
ss << std::quoted(in);
std::string out;
ss >> std::quoted(out);
std::cout << '{' << in << "}\n"
<< '{' << ss.str() << "}\n"
<< '{' << out << "}\n";
}
gives
{\Hello "Wörld"\
}
{"\\Hello \"Wörld\"\\
"}
{\Hello "Wörld"\
}
As described in its proposal, it was really designed for round-tripping of strings.
Using C++11 you can create user defined literals like this:
#include <iostream>
#include <string>
#include <cstddef>
// Define user defined literal "_quoted" operator.
std::string operator"" _quoted(const char* text, std::size_t len) {
return "\"" + std::string(text, len) + "\"";
}
int main() {
std::cout << "tblCustomers"_quoted << std::endl;
std::cout << "cCustomerName"_quoted << std::endl;
}
Output:
"tblCustomers"
"cCustomerName"
You can even define the operator with a shorter name if you want, e.g.:
std::string operator"" _q(const char* text, std::size_t len) { /* ... */ }
// ...
std::cout << "tblCustomers"_q << std::endl;
More info on user-defined literals
String str = "tblCustomers";
str = "'" + str + "'";
See more options here
No standard function, unless you count std::basic_string::operator+(), but writing it is trivial.
I'm somewhat confused by what's slowing you down - quoted( "cCustomerName" ) is more characters, no? :>
You could use your own placeholder character to stand for the quote, some ASCII symbol that will never be used, and replace it with " just before you output the strings.
#include <iostream>
#include <string>
struct quoted
{
const char * _text;
quoted( const char * text ) : _text(text) {}
operator std::string () const
{
std::string quotedStr = "\"";
quotedStr += _text;
quotedStr += "\"";
return quotedStr;
}
};
std::ostream & operator<< ( std::ostream & ostr, const quoted & q )
{
ostr << "\"" << q._text << "\"";
return ostr;
}
int main ( int argc, char * argv[] )
{
std::string strq = quoted( "tblCustomers" );
std::cout << strq << std::endl;
std::cout << quoted( "cCustomerName" ) << std::endl;
return 0;
}
With this you get what you want.
What about using some C function and backslash to escape the quotes?
Like sprintf_s:
#define BUF_SIZE 100
void execute_prog() {
//Strings that will be predicted by quotes
string param1 = "C:\\users\\foo\\input file.txt", string param2 = "output.txt";
//Char array with any buffer size
char command[BUF_SIZE];
//Concating my prog call in the C string.
//sprintf_s requires a buffer size for security reasons
sprintf_s(command, BUF_SIZE, "program.exe \"%s\" \"%s\"", param1.c_str(),
param2.c_str());
system(command);
}
Resulting string is:
program.exe "C:\users\foo\input file.txt" "output.txt"
Here is the documentation.
How do I implement the following (Python pseudocode) in C++?
if argv[1].startswith('--foo='):
foo_value = int(argv[1][len('--foo='):])
(For example, if argv[1] is --foo=98, then foo_value is 98.)
Update: I'm hesitant to look into Boost, since I'm just looking at making a very small change to a simple little command-line tool (I'd rather not have to learn how to link in and use Boost for a minor change).
Use rfind overload that takes the search position pos parameter, and pass zero for it:
std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) { // pos=0 limits the search to the prefix
// s starts with prefix
}
Who needs anything else? Pure STL!
Many have misread this to mean "search backwards through the whole string looking for the prefix". That would give the wrong result (e.g. string("tititito").rfind("titi") returns 2 so when compared against == 0 would return false) and it would be inefficient (looking through the whole string instead of just the start). But it does not do that because it passes the pos parameter as 0, which limits the search to only match at that position or earlier. For example:
std::string test = "0123123";
size_t match1 = test.rfind("123"); // returns 4 (rightmost match)
size_t match2 = test.rfind("123", 2); // returns 1 (skipped over later match)
size_t match3 = test.rfind("123", 0); // returns std::string::npos (i.e. not found)
You would do it like this:
std::string prefix("--foo=");
if (!arg.compare(0, prefix.size(), prefix))
foo_value = std::stoi(arg.substr(prefix.size()));
Looking for a lib such as Boost.ProgramOptions that does this for you is also a good idea.
Just for completeness, I will mention the C way to do it:
If str is your original string, substr is the substring you want to
check, then
strncmp(str, substr, strlen(substr))
will return 0 if str
starts with substr. The functions strncmp and strlen are in the C
header file <string.h>
(originally posted by Yaseen Rauf here, markup added)
For a case-insensitive comparison, use strnicmp instead of strncmp.
This is the C way to do it, for C++ strings you can use the same function like this:
strncmp(str.c_str(), substr.c_str(), substr.size())
If you're already using Boost, you can do it with boost string algorithms + boost lexical cast:
#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp>
try {
if (boost::starts_with(argv[1], "--foo="))
foo_value = boost::lexical_cast<int>(argv[1]+6);
} catch (boost::bad_lexical_cast) {
// bad parameter
}
This kind of approach, like many of the other answers provided here is ok for very simple tasks, but in the long run you are usually better off using a command line parsing library. Boost has one (Boost.Program_options), which may make sense if you happen to be using Boost already.
Otherwise a search for "c++ command line parser" will yield a number of options.
Code I use myself:
std::string prefix = "-param=";
std::string argument = argv[1];
if(argument.substr(0, prefix.size()) == prefix) {
std::string argumentValue = argument.substr(prefix.size());
}
Nobody used the STL algorithm/mismatch function yet. If this returns true, prefix is a prefix of 'toCheck':
std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()
Full example prog:
#include <algorithm>
#include <string>
#include <iostream>
int main(int argc, char** argv) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
<< "Will print true if 'prefix' is a prefix of string" << std::endl;
return -1;
}
std::string prefix(argv[1]);
std::string toCheck(argv[2]);
if (prefix.length() > toCheck.length()) {
std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
<< "'prefix' is longer than 'string'" << std::endl;
return 2;
}
if (std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()) {
std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck << '"' << std::endl;
return 0;
} else {
std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"' << toCheck << '"' << std::endl;
return 1;
}
}
Edit:
As #James T. Huggett suggests, std::equal is a better fit for the question: Is A a prefix of B? and is slight shorter code:
std::equal(prefix.begin(), prefix.end(), toCheck.begin())
Full example prog:
#include <algorithm>
#include <string>
#include <iostream>
int main(int argc, char **argv) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
<< "Will print true if 'prefix' is a prefix of string"
<< std::endl;
return -1;
}
std::string prefix(argv[1]);
std::string toCheck(argv[2]);
if (prefix.length() > toCheck.length()) {
std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
<< "'prefix' is longer than 'string'" << std::endl;
return 2;
}
if (std::equal(prefix.begin(), prefix.end(), toCheck.begin())) {
std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck
<< '"' << std::endl;
return 0;
} else {
std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"'
<< toCheck << '"' << std::endl;
return 1;
}
}
With C++17 you can use std::basic_string_view & with C++20 std::basic_string::starts_with or std::basic_string_view::starts_with.
The benefit of std::string_view in comparison to std::string - regarding memory management - is that it only holds a pointer to a "string" (contiguous sequence of char-like objects) and knows its size. Example without moving/copying the source strings just to get the integer value:
#include <exception>
#include <iostream>
#include <string>
#include <string_view>
int main()
{
constexpr auto argument = "--foo=42"; // Emulating command argument.
constexpr auto prefix = "--foo=";
auto inputValue = 0;
constexpr auto argumentView = std::string_view(argument);
if (argumentView.starts_with(prefix))
{
constexpr auto prefixSize = std::string_view(prefix).size();
try
{
// The underlying data of argumentView is nul-terminated, therefore we can use data().
inputValue = std::stoi(argumentView.substr(prefixSize).data());
}
catch (std::exception & e)
{
std::cerr << e.what();
}
}
std::cout << inputValue; // 42
}
Given that both strings — argv[1] and "--foo" — are C strings, #FelixDombek's answer is hands-down the best solution.
Seeing the other answers, however, I thought it worth noting that, if your text is already available as a std::string, then a simple, zero-copy, maximally efficient solution exists that hasn't been mentioned so far:
const char * foo = "--foo";
if (text.rfind(foo, 0) == 0)
foo_value = text.substr(strlen(foo));
And if foo is already a string:
std::string foo("--foo");
if (text.rfind(foo, 0) == 0)
foo_value = text.substr(foo.length());
Starting with C++20, you can use the starts_with method.
std::string s = "abcd";
if (s.starts_with("abc")) {
...
}
text.substr(0, start.length()) == start
Using STL this could look like:
std::string prefix = "--foo=";
std::string arg = argv[1];
if (prefix.size()<=arg.size() && std::equal(prefix.begin(), prefix.end(), arg.begin())) {
std::istringstream iss(arg.substr(prefix.size()));
iss >> foo_value;
}
At the risk of being flamed for using C constructs, I do think this sscanf example is more elegant than most Boost solutions. And you don't have to worry about linkage if you're running anywhere that has a Python interpreter!
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
for (int i = 1; i != argc; ++i) {
int number = 0;
int size = 0;
sscanf(argv[i], "--foo=%d%n", &number, &size);
if (size == strlen(argv[i])) {
printf("number: %d\n", number);
}
else {
printf("not-a-number\n");
}
}
return 0;
}
Here's some example output that demonstrates the solution handles leading/trailing garbage as correctly as the equivalent Python code, and more correctly than anything using atoi (which will erroneously ignore a non-numeric suffix).
$ ./scan --foo=2 --foo=2d --foo='2 ' ' --foo=2'
number: 2
not-a-number
not-a-number
not-a-number
I use std::string::compare wrapped in utility method like below:
static bool startsWith(const string& s, const string& prefix) {
return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0;
}
C++20 update :
Use std::string::starts_with
https://en.cppreference.com/w/cpp/string/basic_string/starts_with
std::string str_value = /* smthg */;
const auto starts_with_foo = str_value.starts_with(std::string_view{"foo"});
In C++20 now there is starts_with available as a member function of std::string defined as:
constexpr bool starts_with(string_view sv) const noexcept;
constexpr bool starts_with(CharT c) const noexcept;
constexpr bool starts_with(const CharT* s) const;
So your code could be something like this:
std::string s{argv[1]};
if (s.starts_with("--foo="))
In case you need C++11 compatibility and cannot use boost, here is a boost-compatible drop-in with an example of usage:
#include <iostream>
#include <string>
static bool starts_with(const std::string str, const std::string prefix)
{
return ((prefix.size() <= str.size()) && std::equal(prefix.begin(), prefix.end(), str.begin()));
}
int main(int argc, char* argv[])
{
bool usage = false;
unsigned int foos = 0; // default number of foos if no parameter was supplied
if (argc > 1)
{
const std::string fParamPrefix = "-f="; // shorthand for foo
const std::string fooParamPrefix = "--foo=";
for (unsigned int i = 1; i < argc; ++i)
{
const std::string arg = argv[i];
try
{
if ((arg == "-h") || (arg == "--help"))
{
usage = true;
} else if (starts_with(arg, fParamPrefix)) {
foos = std::stoul(arg.substr(fParamPrefix.size()));
} else if (starts_with(arg, fooParamPrefix)) {
foos = std::stoul(arg.substr(fooParamPrefix.size()));
}
} catch (std::exception& e) {
std::cerr << "Invalid parameter: " << argv[i] << std::endl << std::endl;
usage = true;
}
}
}
if (usage)
{
std::cerr << "Usage: " << argv[0] << " [OPTION]..." << std::endl;
std::cerr << "Example program for parameter parsing." << std::endl << std::endl;
std::cerr << " -f, --foo=N use N foos (optional)" << std::endl;
return 1;
}
std::cerr << "number of foos given: " << foos << std::endl;
}
Why not use gnu getopts? Here's a basic example (without safety checks):
#include <getopt.h>
#include <stdio.h>
int main(int argc, char** argv)
{
option long_options[] = {
{"foo", required_argument, 0, 0},
{0,0,0,0}
};
getopt_long(argc, argv, "f:", long_options, 0);
printf("%s\n", optarg);
}
For the following command:
$ ./a.out --foo=33
You will get
33
Ok why the complicated use of libraries and stuff? C++ String objects overload the [] operator, so you can just compare chars.. Like what I just did, because I want to list all files in a directory and ignore invisible files and the .. and . pseudofiles.
while ((ep = readdir(dp)))
{
string s(ep->d_name);
if (!(s[0] == '.')) // Omit invisible files and .. or .
files.push_back(s);
}
It's that simple..
You can also use strstr:
if (strstr(str, substr) == substr) {
// 'str' starts with 'substr'
}
but I think it's good only for short strings because it has to loop through the whole string when the string doesn't actually start with 'substr'.
With C++11 or higher you can use find() and find_first_of()
Example using find to find a single char:
#include <string>
std::string name = "Aaah";
size_t found_index = name.find('a');
if (found_index != std::string::npos) {
// Found string containing 'a'
}
Example using find to find a full string & starting from position 5:
std::string name = "Aaah";
size_t found_index = name.find('h', 3);
if (found_index != std::string::npos) {
// Found string containing 'h'
}
Example using the find_first_of() and only the first char, to search at the start only:
std::string name = ".hidden._di.r";
size_t found_index = name.find_first_of('.');
if (found_index == 0) {
// Found '.' at first position in string
}
More about find
More about find_first_of
Good luck!
std::string text = "--foo=98";
std::string start = "--foo=";
if (text.find(start) == 0)
{
int n = stoi(text.substr(start.length()));
std::cout << n << std::endl;
}
Since C++11 std::regex_search can also be used to provide even more complex expressions matching. The following example handles also floating numbers thorugh std::stof and a subsequent cast to int.
However the parseInt method shown below could throw a std::invalid_argument exception if the prefix is not matched; this can be easily adapted depending on the given application:
#include <iostream>
#include <regex>
int parseInt(const std::string &str, const std::string &prefix) {
std::smatch match;
std::regex_search(str, match, std::regex("^" + prefix + "([+-]?(?=\\.?\\d)\\d*(?:\\.\\d*)?(?:[Ee][+-]?\\d+)?)$"));
return std::stof(match[1]);
}
int main() {
std::cout << parseInt("foo=13.3", "foo=") << std::endl;
std::cout << parseInt("foo=-.9", "foo=") << std::endl;
std::cout << parseInt("foo=+13.3", "foo=") << std::endl;
std::cout << parseInt("foo=-0.133", "foo=") << std::endl;
std::cout << parseInt("foo=+00123456", "foo=") << std::endl;
std::cout << parseInt("foo=-06.12e+3", "foo=") << std::endl;
// throw std::invalid_argument
// std::cout << parseInt("foo=1", "bar=") << std::endl;
return 0;
}
The kind of magic of the regex pattern is well detailed in the following answer.
EDIT: the previous answer did not performed the conversion to integer.
if(boost::starts_with(string_to_search, string_to_look_for))
intval = boost::lexical_cast<int>(string_to_search.substr(string_to_look_for.length()));
This is completely untested. The principle is the same as the Python one. Requires Boost.StringAlgo and Boost.LexicalCast.
Check if the string starts with the other string, and then get the substring ('slice') of the first string and convert it using lexical cast.
C# has a syntax feature where you can concatenate many data types together on 1 line.
string s = new String();
s += "Hello world, " + myInt + niceToSeeYouString;
s += someChar1 + interestingDecimal + someChar2;
What would be the equivalent in C++? As far as I can see, you'd have to do it all on separate lines as it doesn't support multiple strings/variables with the + operator. This is OK, but doesn't look as neat.
string s;
s += "Hello world, " + "nice to see you, " + "or not.";
The above code produces an error.
#include <sstream>
#include <string>
std::stringstream ss;
ss << "Hello, world, " << myInt << niceToSeeYouString;
std::string s = ss.str();
Take a look at this Guru Of The Week article from Herb Sutter: The String Formatters of Manor Farm
In 5 years nobody has mentioned .append?
#include <string>
std::string s;
s.append("Hello world, ");
s.append("nice to see you, ");
s.append("or not.");
s += "Hello world, " + "nice to see you, " + "or not.";
Those character array literals are not C++ std::strings - you need to convert them:
s += string("Hello world, ") + string("nice to see you, ") + string("or not.");
To convert ints (or any other streamable type) you can use a boost lexical_cast or provide your own function:
template <typename T>
string Str( const T & t ) {
ostringstream os;
os << t;
return os.str();
}
You can now say things like:
string s = string("The meaning is ") + Str( 42 );
Your code can be written as1,
s = "Hello world," "nice to see you," "or not."
...but I doubt that's what you're looking for. In your case, you are probably looking for streams:
std::stringstream ss;
ss << "Hello world, " << 42 << "nice to see you.";
std::string s = ss.str();
1 "can be written as" : This only works for string literals. The concatenation is done by the compiler.
Using C++14 user defined literals and std::to_string the code becomes easier.
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;
Note that concatenating string literals can be done at compile time. Just remove the +.
str += "Hello World, " "nice to see you, " "or not";
In C++20 you'll be able to do:
auto s = std::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);
Until then you could do the same with the {fmt} library:
auto s = fmt::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);
Disclaimer: I'm the author of {fmt}.
To offer a solution that is more one-line-ish: A function concat can be implemented to reduce the "classic" stringstream based solution to a single statement.
It is based on variadic templates and perfect forwarding.
Usage:
std::string s = concat(someObject, " Hello, ", 42, " I concatenate", anyStreamableType);
Implementation:
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
or std::stringstream
std::stringstream msg;
msg << "Hello world, " << myInt << niceToSeeYouString;
msg.str(); // returns std::string object
auto s = string("one").append("two").append("three")
The actual problem was that concatenating string literals with + fails in C++:
string s;
s += "Hello world, " + "nice to see you, " + "or not.";
The above code produces an error.
In C++ (also in C), you concatenate string literals by just placing them right next to each other:
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.";
This makes sense, if you generate code in macros:
#define TRACE(arg) cout << #arg ":" << (arg) << endl;
...a simple macro that can be used like this
int a = 5;
TRACE(a)
a += 7;
TRACE(a)
TRACE(a+7)
TRACE(17*11)
(live demo ...)
or, if you insist in using the + for string literals (as already suggested by underscore_d):
string s = string("Hello world, ")+"nice to see you, "+"or not.";
Another solution combines a string and a const char* for each concatenation step
string s;
s += "Hello world, "
s += "nice to see you, "
s += "or not.";
You would have to define operator+() for every data type you would want to concenate to the string, yet since operator<< is defined for most types, you should use std::stringstream.
Damn, beat by 50 seconds...
If you write out the +=, it looks almost the same as C#
string s("Some initial data. "); int i = 5;
s = s + "Hello world, " + "nice to see you, " + to_string(i) + "\n";
As others said, the main problem with the OP code is that the operator + does not concatenate const char *; it works with std::string, though.
Here's another solution that uses C++11 lambdas and for_each and allows to provide a separator to separate the strings:
#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();
}
Usage:
std::vector<std::string> strings { "a", "b", "c" };
std::string joinedStrings = join(", ", strings);
It seems to scale well (linearly), at least after a quick test on my computer; here's a quick test I've written:
#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 (milliseconds):
join(10) : 2
join(100) : 10
join(1000) : 91
join(10000): 898
Here's the one-liner solution:
#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;
}
Although it's a tiny bit ugly, I think it's about as clean as you cat get in C++.
We are casting the first argument to a std::string and then using the (left to right) evaluation order of operator+ to ensure that its left operand is always a std::string. In this manner, we concatenate the std::string on the left with the const char * operand on the right and return another std::string, cascading the effect.
Note: there are a few options for the right operand, including const char *, std::string, and char.
It's up to you to decide whether the magic number is 13 or 6227020800.
Maybe you like my "Streamer" solution to really do it in one line:
#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;
}
You may use this header for this regard: https://github.com/theypsilon/concat
using namespace concat;
assert(concat(1,2,3,4,5) == "12345");
Under the hood you will be using a std::ostringstream.
If you are willing to use c++11 you can utilize user-defined string literals and define two function templates that overload the plus operator for a std::string object and any other object. The only pitfall is not to overload the plus operators of std::string, otherwise the compiler doesn't know which operator to use. You can do this by using the template std::enable_if from type_traits. After that strings behave just like in Java or C#. See my example implementation for details.
Main code
#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;
}
File c_sharp_strings.hpp
Include this header file in all all places where you want to have these 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
Something like this works for me
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);
Based on above solutions I made a class var_string for my project to make life easy. Examples:
var_string x("abc %d %s", 123, "def");
std::string y = (std::string)x;
const char *z = x.c_str();
The class itself:
#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];
};
Still wondering if there will be something better in C++ ?
In c11:
void printMessage(std::string&& message) {
std::cout << message << std::endl;
return message;
}
this allow you to create function call like this:
printMessage("message number : " + std::to_string(id));
will print : message number : 10
you can also "extend" the string class and choose the operator you prefer ( <<, &, |, etc ...)
Here is the code using operator<< to show there is no conflict with streams
note: if you uncomment s1.reserve(30), there is only 3 new() operator requests (1 for s1, 1 for s2, 1 for reserve ; you can't reserve at constructor time unfortunately); without reserve, s1 has to request more memory as it grows, so it depends on your compiler implementation grow factor (mine seems to be 1.5, 5 new() calls in this example)
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;
}
Stringstream with a simple preproccessor macro using a lambda function seems nice:
#include <sstream>
#define make_string(args) []{std::stringstream ss; ss << args; return ss;}()
and then
auto str = make_string("hello" << " there" << 10 << '$');
This works for me:
#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;
}
Output:
c:\example\myfile.txt
Have you tried to avoid the +=?
instead use var = var + ...
it worked for me.
#include <iostream.h> // for string
string myName = "";
int _age = 30;
myName = myName + "Vincent" + "Thorpe" + 30 + " " + 2019;