This question already has answers here:
How to construct a std::string with embedded values, i.e. "string interpolation"?
(8 answers)
Closed 2 years ago.
I am currently learning C++ and I cannot find how to create a string with a formatter to take multiple parameters such as using sprintf but for an unknown string length.
What I want do is something like
string myString = string.format("Hello, here is %s %s and another %s", par1, par2, par3);
I know in C there is the asprintf function where it works out how long the string will be and it malloc's the memory and you need to free it when finished, however, this doesn't seem to be available for C++, although sprintf is. Everything I've seen on google about asprintf seems to mostly focus on Linux, whereas I need cross platform.
Everything I've seen about C++ and string formatting you need to define a char array of fixed length and use sprintf, but I am not going to know the length of the string so I can't do this.
In addition to the existing excellent answer, you may consider the Boost Format library.
Example:
std::string myString = str(boost::format("Hello, here is %s %s an another %s") % par1 % par2 % par3);
Get the book The Standard C++ Library by Josuttis. It will give you the complete string interface and much, much more. You appear to be thinking C, not C++. You could of course use a C interface like sprintf() to load a
char[] and then copy that to a C++ string. That is usually a bad idea.
Two ways to do what you ask:
string myString("Hello, here is ");
myString += par1;
myString += " ";
myString += par2;
myString += " and another ";
myString += par3;
stringstream foo;
foo << "Hello, here is " << par1 << " " << par2 << " and another " << par3;
string myString(foo.str());
There are lots of answers.
As C++ strings get very long, you want to use the std::stringstream to build them. This allows you to write to a string as though it were a file in memory, and it is written to handle very large strings efficiently. The C function snprintf() returns the number of characters it would have written if passed a null pointer. So you need to call it twice, once to get the size, then allocate the buffer and call again to format. It's a good solution for strings which are expected to be quite short and with a defined format, but might get arbitrarily long, like a string containing someone's name.
Note that printf() formats are convenient and easy to use for basic output of integers, string, and reals, but they don't scale up to user-defined objects because there's no accepted way of writing a toString() method and destroying the string after the call. They also can't handle arrays of objects. There is an accepted convention that overloading << writes a text representation of an object to a stream.
Related
Many topics have discussed the difference between string and char[]. However, they are not clear to me to understand why we need to bring string in c++? Any insight is welcome, thanks!
char[] is C style. It is not object oriented, it forces you as the programmer to deal with implementation details (such as '\0' terminator) and rewrite standard code for handling strings every time over and over.
char[] is just an array of bytes, which can be used to store a string, but it is not a string in any meaningful way.
std::string is a class that properly represents a string and handles all string operations.
It lets you create objects and keep your code fully OOP (if that is what you want).
More importantly, it takes care of memory management for you.
Consider this simple piece of code:
// extract to string
#include <iostream>
#include <string>
main ()
{
std::string name;
std::cout << "Please, enter your name: ";
std::cin >> name;
std::cout << "Hello, " << name << "!\n";
return 0;
}
How would you write the same thing using char[]?
Assume you can not know in advance how long the name would be!
Same goes for string concatenation and other operations.
With real string represented as std::string you combine two strings with a simple += operator. One line.
If you are using char[] however, you need to do the following:
Calculate the size of the combined string + terminator character.
Allocate memory for the new combined string.
Use strncpy to copy first string to new array.
Use strncat to append second string to first string in new array.
Plus, you need to remember not to use the unsafe strcpy and strcat and to free the memory once you are done with the new string.
std::string saves you all that hassle and the many bugs you can introduce while writing it.
As noted by MSalters in a comment, strings can grow. This is, in my opinion, the strongest reason to have them in C++.
For example, the following code has a bug which may cause it to crash, or worse, to appear to work correctly:
char message[] = "Hello";
strcat(message, "World");
The same idea with std::string behaves correctly:
std::string message{"Hello"};
message += "World";
Additional benefits of std::string:
You can send it to functions by value, while char[] can only be sent by reference; this point looks rather insignificant, but it enables powerful code like std::vector<std::string> (a list of strings which you can add to)
std::string stores its length, so any operation which needs the length is more efficient
std::string works similarly to all other C++ containers (vector, etc) so if you are already familiar with containers, std::string is easy to use
std::string has overloaded comparison operators, so it's easy to use with std::map, std::sort, etc.
String class is no more than an amelioration of the char[] variable.
With strings you can achieve the same goals than the use of a char[] variable, but you won't have to matter about little tricks of char[] like pointers, segmentation faults...
This is a more convenient way to build strings, but you don't really see the "undergrounds" of the language, like how to implement concatenation or length functions...
Here is the documentation of the std::string class in C++ : C++ string documentation
tl:dr
How can I concatenate const char* with std::string, neatly and
elegantly, without multiple function calls. Ideally in one function
call and have the output be a const char*. Is this impossible, what
is an optimum solution?
Initial Problem
The biggest barrier I have experienced with C++ so far is how it handles strings. In my opinion, of all the widely used languages, it handles strings the most poorly. I've seen other questions similar to this that either have an answer saying "use std::string" or simply point out that one of the options is going to be best for your situation.
However this is useless advice when trying to use strings dynamically like how they are used in other languages. I cannot guaranty to always be able to use std::string and for the times when I have to use const char* I hit the obvious wall of "it's constant, you can't concatenate it".
Every solution to any string manipulation problem I've seen in C++ requires repetitive multiple lines of code that only work well for that format of string.
I want to be able to concatenate any set of characters with the + symbol or make use of a simple format() function just how I can in C# or Python. Why is there no easy option?
Current Situation
Standard Output
I'm writing a DLL and so far I've been output text to cout via the << operator. Everything has been going fine so far using simple char arrays in the form:
cout << "Hello world!"
Runtime Strings
Now it comes to the point where I want to construct a string at runtime and store it with a class, this class will hold a string that reports on some errors so that they can be picked up by other classes and maybe sent to cout later, the string will be set by the function SetReport(const char* report). So I really don't want to use more than one line for this so I go ahead and write something like:
SetReport("Failure in " + __FUNCTION__ + ": foobar was " + foobar + "\n"); // __FUNCTION__ gets the name of the current function, foobar is some variable
Immediately of course I get:
expression must have integral or unscoped enum type and...
'+': cannot add two pointers
Ugly Strings
Right. So I'm trying to add two or more const char*s together and this just isn't an option. So I find that the main suggestion here is to use std::string, sort of weird that typing "Hello world!" doesn't just give you one of those in the first place but let's give it a go:
SetReport(std::string("Failure in ") + std::string(__FUNCTION__) + std::string(": foobar was ") + std::to_string(foobar) + std::string("\n"));
Brilliant! It works! But look how ugly that is!! That's some of the ugliest code I've every seen. We can simplify to this:
SetReport(std::string("Failure in ") + __FUNCTION__ + ": foobar was " + std::to_string(foobar) + "\n");
Still possibly the worst way I've every encounter of getting to a simple one line string concatenation but everything should be fine now right?
Convert Back To Constant
Well no, if you're working on a DLL, something that I tend to do a lot because I like to unit test so I need my C++ code to be imported by the unit test library, you will find that when you try to set that report string to a member variable of a class as a std::string the compiler throws a warning saying:
warning C4251: class 'std::basic_string<_Elem,_Traits,_Alloc>' needs to have dll-interface to be used by clients of class'
The only real solution to this problem that I've found other than "ignore the warning"(bad practice!) is to use const char* for the member variable rather than std::string but this is not really a solution, because now you have to convert your ugly concatenated (but dynamic) string back to the const char array you need. But you can't just tag .c_str() on the end (even though why would you want to because this concatenation is becoming more ridiculous by the second?) you have to make sure that std::string doesn't clean up your newly constructed string and leave you with garbage. So you have to do this inside the function that receives the string:
const std::string constString = (input);
m_constChar = constString.c_str();
Which is insane. Because now I traipsed across several different types of string, made my code ugly, added more lines than should need and all just to stick some characters together. Why is this so hard?
Solution?
So what's the solution? I feel that I should be able to make a function that concatenates const char*s together but also handle other object types such as std::string, int or double, I feel strongly that this should be capable in one line, and yet I'm unable to find any examples of it being achieved. Should I be working with char* rather than the constant variant, even though I've read that you should never change the value of char* so how would this help?
Are there any experienced C++ programmers who have resolved this issue and are now comfortable with C++ strings, what is your solution? Is there no solution? Is it impossible?
The standard way to build a string, formatting non-string types as strings, is a string stream
#include <sstream>
std::ostringstream ss;
ss << "Failure in " << __FUNCTION__ << ": foobar was " << foobar << "\n";
SetReport(ss.str());
If you do this often, you could write a variadic template to do that:
template <typename... Ts> std::string str(Ts&&...);
SetReport(str("Failure in ", __FUNCTION__, ": foobar was ", foobar, '\n'));
The implementation is left as an exercise for the reader.
In this particular case, string literals (including __FUNCTION__) can be concatenated by simply writing one after the other; and, assuming foobar is a std::string, that can be concatenated with string literals using +:
SetReport("Failure in " __FUNCTION__ ": foobar was " + foobar + "\n");
If foobar is a numeric type, you could use std::to_string(foobar) to convert it.
Plain string literals (e.g. "abc" and __FUNCTION__) and char const* do not support concatenation. These are just plain C-style char const[] and char const*.
Solutions are to use some string formatting facilities or libraries, such as:
std::string and concatenation using +. May involve too many unnecessary allocations, unless operator+ employs expression templates.
std::snprintf. This one does not allocate buffers for you and not type safe, so people end up creating wrappers for it.
std::stringstream. Ubiquitous and standard but its syntax is at best awkward.
boost::format. Type safe but reportedly slow.
cppformat. Reportedly modern and fast.
One of the simplest solution is to use an C++ empty string. Here I declare empty string variable named _ and used it in front of string concatenation. Make sure you always put it in the front.
#include <cstdio>
#include <string>
using namespace std;
string _ = "";
int main() {
char s[] = "chararray";
string result =
_ + "function name = [" + __FUNCTION__ + "] "
"and s is [" + s + "]\n";
printf( "%s", result.c_str() );
return 0;
}
Output:
function name = [main] and s is [chararray]
Regarding __FUNCTION__, I found that in Visual C++ it is a macro while in GCC it is a variable, so SetReport("Failure in " __FUNCTION__ "; foobar was " + foobar + "\n"); will only work on Visual C++. See: https://msdn.microsoft.com/en-us/library/b0084kay.aspx and https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html
The solution using empty string variable above should work on both Visual C++ and GCC.
My Solution
I've continued to experiment with different things and I've got a solution which combines tivn's answer that involves making an empty string to help concatenate long std::string and character arrays together and a function of my own which allows single line copying of that std::string to a const char* which is safe to use when the string object leaves scope.
I would have used Mike Seymour's variadic templates but they don't seem to be supported by the Visual Studio 2012 I'm running and I need this solution to be very general so I can't rely on them.
Here is my solution:
Strings.h
#ifndef _STRINGS_H_
#define _STRINGS_H_
#include <string>
// tivn's empty string in the header file
extern const std::string _;
// My own version of .c_str() which produces a copy of the contents of the string input
const char* ToCString(std::string input);
#endif
Strings.cpp
#include "Strings.h"
const std::string str = "";
const char* ToCString(std::string input)
{
char* result = new char[input.length()+1];
strcpy_s(result, input.length()+1, input.c_str());
return result;
}
Usage
m_someMemberConstChar = ToCString(_ + "Hello, world! " + someDynamicValue);
I think this is pretty neat and works in most cases. Thank you everyone for helping me with this.
As of C++20, fmtlib has made its way into the ISO standard but, even on older iterations, you can still download and use it.
It gives similar capabilities as Python's str.format()(a), and your "ugly strings" example then becomes a relatively simple:
#include <fmt/format.h>
// Later on, where code is allowed (inside a function for example) ...
SetReport(fmt::format("Failure in {}: foobar was {}\n", __FUNCTION__, foobar));
It's much like the printf() family but with extensibility and type safety built in.
(a) But, unfortunately, not its string interpolation feature (use of f-strings), which has the added advantage of putting the expressions in the string at the place where they're output, something like:
set_report(f"Failure in {__FUNCTION__}: foobar was {foobar}\n");
If fmtlib ever got that capability, I'd probably wet my pants in excitement :-)
When should I use stringstream instead of string::append()?
Supposing I'm going to catenate just strings.
stringstream ss;
ss << str1 << "str2" << ...
Write(ss.str());
Or:
string str;
str.reserve(10000);
str.append(str1);
str.append("str2");
...
Write(str);
Which of them is faster?
I don't know which one will be faster, but if I had to guess I'd say your second example is, especially since you've called the reserve member function to allocate a large space for expansion.
If you're only concatenating strings use string::append (or string::operator+=).
If you're going to convert numbers to their string representation, as well as format them during conversion, and then append the conversion results together, use stringstreams. I mention the formatting part explicitly because if you do not require formatting C++11 offers std::to_string which can be used to convert numeric types to strings.
string.append is much faster. Especially when you reserve.
If you are concatenating only strings, I would use string.append. I would only use stringstream when I need to automatically convert non-strings to strings for example:
const int x(42);
stringstream ss;
ss << "My favorite number is: " << x << std::endl;
Here stringstream automatically converts x to a string and appends it. I do not need to call atoi. Stringstream will convert all the basic types automatically for you. It is great for that purpose.
Also if you are only going to be directing data into the stringstream to convert it to a string later. You can use ostringstream which is for output.
I hope that helps.
Ps: This is more of a conceptual question.
I know this makes things more complicated for no good reason, but here is what I'm wondering. If I'm not mistaken, a const char* "like this" in c++ is pointing to l and will be automatically zero terminated on compile time. I believe it is creating a temporary variable const char* to hold it, unless it is keeping track of the offset using a byte variable (I didn't check the disassembly). My question is, how would you if even possible, add characters to this string without having to call functions or instantiating strings?
Example (This is wrong, just so you can visualize what I meant):
"Like thi" + 's';
The closest thing I came up with was to store it to a const char* with enough spaces and change the other characters.
Example:
char str[9];
strcpy(str, "Like thi")
str[8] = 's';
Clarification:
Down vote: This question does not show any research effort; it is unclear or not useful
Ok, so the question has been highly down voted. There wasn't much reasoning on which of these my question was lacking on, so I'll try to improve all of those qualities.
My question was more so I could have a better understanding of what goes on when you simply create a string "like this" without storing the address of that string in a const char* I also wanted to know if it was possible to concatenate/change the content of that string without using functions like strcat() and without using the overloaded operator + from the class string. I'm aware this is not exactly useful for dealing with strings in C++, but I was curious whether or not there was a way besides the standard ways for doing so.
string example = "Like thi" + "s"; //I'm aware of the string class and its member functions
const char* example2 = "Like this"; //I'm also aware of C-type Strings (CString as well)
It is also possible that not having English as my native language made things even worst, I apologize for the confusion.
Instead of using a plain char string, you should use the string library provided by the C++ library:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string str = "Like thi";
cout << str << endl;
str = str + "s";
cout << str << endl;
return 0;
}
Normally, it's not possible to simply concatenate plain char * strings in C or C++, because they are merely pointers to arrays of characters. There's almost no reason you should be using a bare character array in C++ if you intend on doing any string manipulations within your own code.
Even if you need access to the C representation (e.g. for an external library) you can use string::c_str().
First, there is nothing null terminated, but the zero terminated. All char* strings in C end with '\0'.
When you in code do something like this:
char *name="Daniel";
compiler will generate a string that has a contents:
Daniel\0
and will initialize name pointer to point at it at a certain time during program execution depending on the variable context (member, static, ...).
Appending ANYTHING to the name won't work as you expect, since memory pointed to by name isn't changeable, and you'll probably get either access violation error or will overwrite something else.
Having
const char* copyOfTheName = name;
won't create a copy of the string in question, it will only have copyOfTheName point to the original string, so having
copyOfTheName[6]='A';
will be exactly as
name[6]='A';
and will only cause problems to you.
Use std::strcat instead. And please, do some investigating how the basic string operations work in C.
How do I concatenate Strings with C/C++?
I tried the following ways:
PS: errorInfo is a char * I should return it.
errorInfo = strcat("Workflow: ", strcat(
workflowToString(workflow).utf8(), strcat(" ERROR: ",
errorCode.utf8)));
sprintf(errorInfo, "Workflow %s ERROR: %s",
workflowToString(workflow).utf8(), errorCode.utf8());
errorInfo = "Workflow: " + workflowToString(workflow).utf8() + " ERROR: " + errorCode.utf8;
Just the sprintf compiles but when running my application crash.
PS: I'm using NDK from Android
There ISN'T such a language as C/C++. There is C, and there is C++.
In C++ you concatenate std::string's by using operator+
In C, you use strcat
I know this doesn't quite answer your question, this is just an outcry :)
According to this page strcat does the following:
Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a new null-character is appended at the end of the new string formed by the concatenation of both in destination.
In your implementation, however, "Workflow: " is a constant string. You cannot modify that string, which is what strcat would do. In order to do that, create a string like:
char message[1000];
strcpy(message, "Workflow: ");
strcat(message, "other string");
....
However, be careful about the utf8 character encoding because one utf8 code point could be multiple chars long.
Concatenation is almost always the wrong idiom for string building, especially in C. It's error-prone, clutters your code, and has extremely bad asymptotic performance (i.e. O(n^2) instead of O(n) for building a string of length n).
Instead you should use the snprintf function, as in:
snprintf(buf, sizeof buf, "Workflow: %s ERROR: %s", workflow, error);
or if you're writing to a file/socket/etc. and don't need to keep the resulting string in memory, simply use fprintf to begin with.
With string literals you can simple use:
char str[] = "foo" " bar";
const char *s = " 1 " " 2 ";
s = " 3 " " 4 ";
By using strcat(), you are working in c, not c++.
c is not going to automatically manage memory for you.
c can be confusing since sometimes it seems like it has a string data type when all it is doing is providing you a string interface to arrays of characters.
For one thing, the first argument to strcat() has to be writable and have enough room to add the second string.
char *out = strcat("This", "nThat");
is asking c to stomp on string literal memory.
In general, you should NEVER use strcat()/sprintf, as in the above "chosen" answer. You can overwrite memory that way. Use strncat()/snprintf() instead to avoid buffer overruns. If you don't know the size to pass to "n" in strncat(), you're likely doing something wrong.
One way to do this in c would be:
#define ERROR_BUF_SIZE 2048 // or something big enough, you have to know in c
char errorInfo[ERROR_BUF_SIZE];
snprintf(errorInfo, ERROR_BUF_SIZE, "Workflow %s ERROR: %s",
workflowToString(workflow).utf8(), errorCode.utf8());
or similarly using strncpy/strncat
There are many ways you can concatenate in C while using Android NDK:
Two ways I used are:
strcat
sprintf
here is example:
enter code here
strcat
char* buffer1=(char*)malloc(250000);
char* buffer2=(char*)malloc(250000);
char* buffer3=(char*)malloc(250000);
buffer1 = strcat(buffer1, buffer2);
sprintf
sprintf(buffer3,"this is buffer1: %s and this is buffer2:%s",buffer1,buffer2);`
sprintf returns length of your string
strcat is not recommended as its use more memory..
you can use sprintf or others like strcpy.
Hope it helps.