我必须用sprintf格式化std::string,并将其发送到文件流。我该怎么做呢?
当前回答
以下是我的(简单的解决方案):
std::string Format(const char* lpszFormat, ...)
{
// Warning : "vsnprintf" crashes with an access violation
// exception if lpszFormat is not a "const char*" (for example, const string&)
size_t nSize = 1024;
char *lpBuffer = (char*)malloc(nSize);
va_list lpParams;
while (true)
{
va_start(lpParams, lpszFormat);
int nResult = vsnprintf(
lpBuffer,
nSize,
lpszFormat,
lpParams
);
va_end(lpParams);
if ((nResult >= 0) && (nResult < (int)nSize) )
{
// Success
lpBuffer[nResult] = '\0';
std::string sResult(lpBuffer);
free (lpBuffer);
return sResult;
}
else
{
// Increase buffer
nSize =
(nResult < 0)
? nSize *= 2
: (nResult + 1)
;
lpBuffer = (char *)realloc(lpBuffer, nSize);
}
}
}
其他回答
c++ 11内部使用vsnprintf()的解决方案:
#include <stdarg.h> // For va_start, etc.
std::string string_format(const std::string fmt, ...) {
int size = ((int)fmt.size()) * 2 + 50; // Use a rubric appropriate for your code
std::string str;
va_list ap;
while (1) { // Maximum two passes on a POSIX system...
str.resize(size);
va_start(ap, fmt);
int n = vsnprintf((char *)str.data(), size, fmt.c_str(), ap);
va_end(ap);
if (n > -1 && n < size) { // Everything worked
str.resize(n);
return str;
}
if (n > -1) // Needed size returned
size = n + 1; // For null char
else
size *= 2; // Guess at a larger size (OS specific)
}
return str;
}
一种更安全、更有效的方法(我测试过,它更快):
#include <stdarg.h> // For va_start, etc.
#include <memory> // For std::unique_ptr
std::string string_format(const std::string fmt_str, ...) {
int final_n, n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */
std::unique_ptr<char[]> formatted;
va_list ap;
while(1) {
formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */
strcpy(&formatted[0], fmt_str.c_str());
va_start(ap, fmt_str);
final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap);
va_end(ap);
if (final_n < 0 || final_n >= n)
n += abs(final_n - n + 1);
else
break;
}
return std::string(formatted.get());
}
fmt_str是按值传递的,以符合va_start的要求。
注意:“更安全”和“更快”的版本在某些系统上不起作用。因此,两家公司仍在上市。此外,“更快”完全取决于预分配步骤是否正确,否则strcpy会使其变慢。
你不能直接这样做,因为你没有对底层缓冲区的写访问权(直到c++ 11;见Dietrich Epp的评论)。你必须先在c-string中执行,然后将其复制到std::string中:
char buff[100];
snprintf(buff, sizeof(buff), "%s", "Hello");
std::string buffAsStdStr = buff;
但我不确定为什么不直接使用字符串流?我想你有特定的理由不这么做:
std::ostringstream stringStream;
stringStream << "Hello";
std::string copyOfStr = stringStream.str();
c++ 17解决方案(这将工作于std::string和std::wstring):
分配一个缓冲区,格式化它,然后复制到另一个字符串是不高效的。可以创建格式化字符串大小的std::string,并直接格式化到字符串缓冲区中:
#include <string>
#include <stdexcept>
#include <cwchar>
#include <cstdio>
#include <type_traits>
template<typename T, typename ... Args>
std::basic_string<T> string_format(T const* const format, Args ... args)
{
int size_signed{ 0 };
// 1) Determine size with error handling:
if constexpr (std::is_same_v<T, char>) { // C++17
size_signed = std::snprintf(nullptr, 0, format, args ...);
}
else {
size_signed = std::swprintf(nullptr, 0, format, args ...);
}
if (size_signed <= 0) {
throw std::runtime_error("error during formatting.");
}
const auto size = static_cast<size_t>(size_signed);
// 2) Prepare formatted string:
std::basic_string<T> formatted(size, T{});
if constexpr (std::is_same_v<T, char>) { // C++17
std::snprintf(formatted.data(), size + 1, format, args ...); // +1 for the '\0' (it will not be part of formatted).
}
else {
std::swprintf(formatted.data(), size + 1, format, args ...); // +1 for the '\0' (it will not be part of formatted).
}
return formatted; // Named Return Value Optimization (NRVO), avoids an unnecessary copy.
}
此外:通常,format参数是char[] / wchar_t[] &创建std::string对象效率不高。传递char*或wchar_t* &如果你已经有一个std::string对象,你仍然可以使用它作为your_string.c_str()。例子:
int main()
{
int i{ 0 };
// The format parameter is a char[] / wchar_t[]:
const std::string title1 = string_format("story[%d].", ++i); // => "story[1]"
const std::wstring title2 = string_format(L"story[%d].", ++i); // => L"story[2]"
// If you already have a std::string object:
const std::string format1{ "story[%d]." };
const std::string title3 = string_format(format1.c_str(), ++i); // => "story[3]"
const std::wstring format2{ L"story[%d]." };
const std::wstring title4 = string_format(format2.c_str(), ++i); // => L"story[4]"
}
这个问题已经解决了。但是,我认为这是c++中格式化字符串的另一种方式
class string_format {
private:
std::string _result;
public:
string_format( ) { }
~string_format( ) { std::string( ).swap( _result ); }
const std::string& get_data( ) const { return _result; }
template<typename T, typename... Targs>
void format( const char* fmt, T value, Targs... Fargs ) {
for ( ; *fmt != '\0'; fmt++ ) {
if ( *fmt == '%' ) {
_result += value;
this->format( fmt + 1, Fargs..., 0 ); // recursive call
return;
}
_result += *fmt;
}
}
friend std::ostream& operator<<( std::ostream& ostream, const string_format& inst );
};
inline std::string& operator+=( std::string& str, int val ) {
str.append( std::to_string( val ) );
return str;
}
inline std::string& operator+=( std::string& str, double val ) {
str.append( std::to_string( val ) );
return str;
}
inline std::string& operator+=( std::string& str, bool val ) {
str.append( val ? "true" : "false" );
return str;
}
inline std::ostream& operator<<( std::ostream& ostream, const string_format& inst ) {
ostream << inst.get_data( );
return ostream;
}
并测试这个类:
string_format fmt;
fmt.format( "Hello % and is working ? Ans: %", "world", true );
std::cout << fmt;
你可以在这里查一下
如果你在一个有asprintf(3)的系统上,你可以很容易地对它进行包装:
#include <iostream>
#include <cstdarg>
#include <cstdio>
std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
std::string format(const char *fmt, ...)
{
std::string result;
va_list ap;
va_start(ap, fmt);
char *tmp = 0;
int res = vasprintf(&tmp, fmt, ap);
va_end(ap);
if (res != -1) {
result = tmp;
free(tmp);
} else {
// The vasprintf call failed, either do nothing and
// fall through (will return empty string) or
// throw an exception, if your code uses those
}
return result;
}
int main(int argc, char *argv[]) {
std::string username = "you";
std::cout << format("Hello %s! %d", username.c_str(), 123) << std::endl;
return 0;
}
推荐文章
- decltype(auto)的一些用途是什么?
- Shared_ptr转换为数组:应该使用它吗?
- Printf与std::字符串?
- 禁用复制构造函数
- 不区分大小写的“in”
- 我如何得到一个字符串的前n个字符而不检查大小或出界?
- 只接受特定类型的c++模板
- 如何在PHP中截断字符串最接近于一定数量的字符?
- c#和Java中的泛型有什么不同?和模板在c++ ?
- Ruby数组到字符串的转换
- c++ 11中的递归lambda函数
- 在c++中指针使用NULL或0(零)吗?
- 为什么在Java和。net中不能修改字符串?
- 在c++中,如何将int值附加到字符串中?
- 如何创建一个日期对象从字符串在javascript