C++: how to get fprintf results as a std::string w/o sprintf
I am using #3: the boost string format library - but I have to admit that I've never had any problem with the differences in format specifications.
Works like a charm for me - and the external dependencies could be worse (a very stable library)
Edited: adding an example how to use boost::format instead of printf:
sprintf(buffer, "This is a string with some %s and %d numbers", "strings", 42);
would be something like this with the boost::format library:
string = boost::str(boost::format("This is a string with some %s and %d numbers") %"strings" %42);
Hope this helps clarify the usage of boost::format
I've used boost::format as a sprintf / printf replacement in 4 or 5 applications (writing formatted strings to files, or custom output to logfiles) and never had problems with format differences. There may be some (more or less obscure) format specifiers which are differently - but I never had a problem.
In contrast I had some format specifications I couldn't really do with streams (as much as I remember)
std::string formatting like sprintf
You can't do it directly, because you don't have write access to the underlying buffer (until C++11; see Dietrich Epp's comment). You'll have to do it first in a c-string, then copy it into a std::string:
char buff[100];
snprintf(buff, sizeof(buff), "%s", "Hello");
std::string buffAsStdStr = buff;
But I'm not sure why you wouldn't just use a string stream? I'm assuming you have specific reasons to not just do this:
std::ostringstream stringStream;
stringStream << "Hello";
std::string copyOfStr = stringStream.str();
Best way to safely printf to a string?
This StackOverflow question has a similar discussion. Also in that question I present my favorite solution, a "format" function that takes identical arguments to printf and returns a std::string.
Proper use of fprintf
It is acceptable if you "know" the string variable to be "clean", if you don't care about the warning most modern compilers generate for that construct. Because:
If your string contains conversion specifiers "by accident", you are invoking undefined behaviour.
If you read that string from somewhere, a malicious attacker could exploit point 1. above to his ends.
It's generally better to use puts()
or fputs()
as they avoid this problem, and consequently don't generate a warning. (puts()
also tosses in an automatic '\n'
.)
The *puts()
functions also have (marginally) better performance. *printf()
, even on nothing more than "%s"
as format string, still has to parse that conversion specifier, and count the number of characters printed for its return value.
Thanks to users 'rici' and 'Grady Player' for pointing out the character counting and compiler warning. My C got a bit rusty it seems. ;-)
printf with std::string?
It's compiling because printf
isn't type safe, since it uses variable arguments in the C sense1. printf
has no option for std::string
, only a C-style string. Using something else in place of what it expects definitely won't give you the results you want. It's actually undefined behaviour, so anything at all could happen.
The easiest way to fix this, since you're using C++, is printing it normally with std::cout
, since std::string
supports that through operator overloading:
std::cout << "Follow this command: " << myString;
If, for some reason, you need to extract the C-style string, you can use the c_str()
method of std::string
to get a const char *
that is null-terminated. Using your example:
#include <iostream>
#include <string>
#include <stdio.h>
int main()
{
using namespace std;
string myString = "Press ENTER to quit program!";
cout << "Come up and C++ me some time." << endl;
printf("Follow this command: %s", myString.c_str()); //note the use of c_str
cin.get();
return 0;
}
If you want a function that is like printf
, but type safe, look into variadic templates (C++11, supported on all major compilers as of MSVC12). You can find an example of one here. There's nothing I know of implemented like that in the standard library, but there might be in Boost, specifically boost::format
.
[1]: This means that you can pass any number of arguments, but the function relies on you to tell it the number and types of those arguments. In the case of printf
, that means a string with encoded type information like %d
meaning int
. If you lie about the type or number, the function has no standard way of knowing, although some compilers have the ability to check and give warnings when you lie.
fprintf() / std::cout doesn't print part of the string to stdout/stderr
I use boost::asio
, I have a callback to read from stdin
. this read is nonblocking
- happens with async_read_some()
.
The problem was stdin
was turned to be nonblocking
, and it also caused stdout
to be nonblocking
as well because they point to the same file description (explanation).
It caused the fprintf()
calls to fail (returned -1 with errno 11
) and not all of the output got printed out on the screen.
It has no relation to boost.
I succeeded isolating the problem, the following code creates this problem:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
using namespace std;
int main(int argc, char *argv[]) {
const int problem = 8000;
const int myBuffSize = 32000;
char *myBuff = new char[myBuffSize];
int myoffset = 0;
memset(myBuff, '-', myBuffSize);
int flags;
bool toogle = true;
bool running = true;
// Comment from here
if ((flags = fcntl(STDIN_FILENO, F_GETFL, 0)) < 0) {
printf("error fcntl()\n");
return 0;
}
if (fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) < 0) {
printf("error fcntl()\n");
return 0;
}
// Comment until here
while(running) {
toogle = toogle ? false : true;
if (toogle) {
snprintf(myBuff + problem, myBuffSize - problem, "fin\n\n");
} else {
snprintf(myBuff + problem, myBuffSize - problem, "end\n\n");
}
fprintf(stdout, "%s", myBuff);
sleep(1);
}
delete[] myBuff;
return 0;
}
If you'll comment the // Comment from here
to // Comment untill here
, it will print all of the output (fin
and end
will be printed).
One solution to this problem is to open another fd
to the current tty
using fopen(ttyname(STDOUT_FILENO), "w")
and to print into it.
I believe another solution is to async_write()
into screen.
Use sprintf with fixed width, left-justified field and concatenation
You could split your sprintf
's (and, in the meantime, use snprintf
instead, for safety), and use the return value of the previous one to know exactly how much characters have been printed.
Something like that :
// Will split in columns = 10, 50, 60
void formatMyString(int num, char *name, int counter, float setval, float mes)
{
char buf[256] = {0};
int cnt = 0;
cnt = snprintf(buf, 256-cnt, "%i ", num);
if (cnt < 10) {
memset(&buf[cnt], ' ', 10-cnt);
cnt = 10;
}
cnt += snprintf(&buf[cnt], 256-cnt, "%s_setup%i ", name, counter);
if (cnt < 50) {
memset(&buf[cnt], ' ', 50-cnt);
cnt = 50;
}
cnt += snprintf(&buf[cnt], 256-cnt, "%2.2f ", setval);
if (cnt < 60) {
memset(&buf[cnt], ' ', 60-cnt);
cnt = 60;
}
snprintf(&buf[cnt], 256-cnt, "%2.2f", mes);
printf("%s\n", buf);
}
This should work. You can change the split columns by changing the constants, and adjust for thour format.
If the inputs are longer than the expected size, then no padding is done.
String format works in fprintf but doesn't work in sprintf, gives segmentation fault
You are not allocating memory for stat
. Try
char *stat = malloc(MAXLEN);
snprintf(stat, MAXLEN, ...);
^ ^
how to specify multiple format string in fprintf function?
Use a for
loop. That will be able to print 100 floating point numbers
fprintf: print literal hex string
You'll have to convert the std::string
to a null-terminated const char*
using its c_str()
member function:
fprintf(stdout,"foo=%s",s.c_str());
Note that this is still C++, even if you use fprintf()
. C does not contain a data type std::string
.
Related Topics
Can Lambda Functions Be Recursive
Making a C++ Class a Monitor (In the Concurrent Sense)
Conversion from Boost::Shared_Ptr to Std::Shared_Ptr
Cmath VS Math.H (And Similar C-Prefixed VS .H Extension Headers)
Why Does This Simple Std::Thread Example Not Work
How to Create Std::Array with Initialization List Without Providing Size Directly
How to Load a Custom Binary Resource in a Vc++ Static Library as Part of a Dll
Literal Initialization for Const References
Lnk2019: Unresolved External Symbol _Main Referenced in Function _Tmaincrtstartup
Do Stl Iterators Guarantee Validity After Collection Was Changed
Initialize Global Array of Function Pointers at Either Compile-Time, or Run-Time Before Main()
Why the Libc++ Std::Vector Internally Keeps Three Pointers Instead of One Pointer and Two Sizes
Cin.Getline() Is Skipping an Input in C++
A Way in C++ to Hide a Specific Function
Why Does Constexpr Static Member (Of Type Class) Require a Definition