我必须用sprintf格式化std::string,并将其发送到文件流。我该怎么做呢?
当前回答
我用vsnprintf写了我自己的,所以它返回字符串,而不是必须创建我自己的缓冲区。
#include <string>
#include <cstdarg>
//missing string printf
//this is safe and convenient but not exactly efficient
inline std::string format(const char* fmt, ...){
int size = 512;
char* buffer = 0;
buffer = new char[size];
va_list vl;
va_start(vl, fmt);
int nsize = vsnprintf(buffer, size, fmt, vl);
if(size<=nsize){ //fail delete buffer and try again
delete[] buffer;
buffer = 0;
buffer = new char[nsize+1]; //+1 for /0
nsize = vsnprintf(buffer, size, fmt, vl);
}
std::string ret(buffer);
va_end(vl);
delete[] buffer;
return ret;
}
所以你可以用它
std::string mystr = format("%s %d %10.5f", "omg", 1, 10.5);
其他回答
我现在将为Visual Studio编写版本,希望有一天有人会让它变得可移植。(怀疑需要用vsnwprintf替换_vsnwprintf之类的东西。)
您需要使用项目配置中的define _CRT_SECURE_NO_WARNINGS来禁用已弃用的警告。
我使用_vsnwprintf与第一个参数作为nullptr能够估计缓冲区大小,保留wstring缓冲区,然后格式化字符串直接到缓冲区。
不确定为什么需要禁用已弃用警告,因为相同方法调用(_vsnwprintf_s)的安全版本不能使用nullptr作为输入。怀疑需要报告给微软c++团队。
这个版本应该同时使用- string类或wstring类。
如果你发现任何错误或不一致,请再问一次,我会尽力修复它。
stringHelpers.h:
#pragma once
#include <string>
//
// Formats string/wstring according to format, if formatting fails (e.g. invalid %s pointer - returns empty string)
//
template <typename T>
std::basic_string<T> sFormat(const T* format, ...)
{
va_list args;
va_start(args, format);
int size;
if constexpr (std::is_same_v<T, char>)
size = vsnprintf(nullptr, 0, format, args);
else
size = _vsnwprintf(nullptr, 0, format, args);
size++; // Zero termination
std::basic_string<T> s;
s.resize(size);
if constexpr (std::is_same_v<T, char>)
vsnprintf(&s[0], size, format, args);
else
_vsnwprintf(&s[0], size, format, args);
va_end(args);
return s;
}
以上是代码示例,可以复制。我将维护工作版本在我自己的仓库在github:
https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/helpers.h#L12
我通常用这个:
std::string myformat(const char *const fmt, ...)
{
char *buffer = NULL;
va_list ap;
va_start(ap, fmt);
(void)vasprintf(&buffer, fmt, ap);
va_end(ap);
std::string result = buffer;
free(buffer);
return result;
}
缺点:并非所有系统都支持vasprint
c++ 20 std::格式
它来了!该特性描述在:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r9.html,并使用类似python的.format()语法。
我希望它的用法是这样的:
#include <format>
#include <string>
int main() {
std::string message = std::format("The answer is {}.", 42);
}
g++-9 -std=c++2a的GCC 9.1.0仍然不支持它。
现有的fmt库在获得官方支持之前实现了它:https://github.com/fmtlib/fmt,如前所述:std::string格式化,如sprintf:
sudo apt install libfmt-dev
修改源代码以替换:
用<fmt/core.h> .h格式化> Std::format转换为fmt::format
main.cpp
#include <string>
#include <iostream>
#include <fmt/core.h>
int main() {
std::string message = fmt::format("The answer is {}.", 42);
std::cout << message << std::endl;
}
编译并运行:
g++ -std=c++11 -o main.out main.cpp -lfmt
./main.out
输出:
The answer is 42.
API将添加一个新的std::format头文件:
建议的格式化API定义在新的header <format>中,应该不会对现有代码产生影响。
十六进制格式{:x}
c++十六进制成本值?
前导零{:03}
用c++输出操作符打印前导零?
左对齐{:<},右对齐{:>},中对齐{:^}
打印cout时c++对齐<<
浮点精度{:.2}
我如何打印一个双值与全精度使用cout? 在c++中设置默认浮点打印精度
在正数{:+}上显示符号
如何打印正数与前缀+在c++
将布尔值显示为true和false: {:}
在c++中将bool转换为文本
以下是我的(简单的解决方案):
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);
}
}
}
从Dacav和pixelpoint的答案中获得灵感。我玩了一下,得到了这个:
#include <cstdarg>
#include <cstdio>
#include <string>
std::string format(const char* fmt, ...)
{
va_list vl;
va_start(vl, fmt);
int size = vsnprintf(0, 0, fmt, vl) + sizeof('\0');
va_end(vl);
char buffer[size];
va_start(vl, fmt);
size = vsnprintf(buffer, size, fmt, vl);
va_end(vl);
return std::string(buffer, size);
}
通过合理的编程实践,我相信代码应该足够了,但是我仍然对更安全的替代方案持开放态度,这些替代方案仍然足够简单,不需要c++ 11。
下面是另一个版本,它使用初始缓冲区来防止在初始缓冲区已经足够多时再次调用vsnprintf()。
std::string format(const char* fmt, ...)
{
va_list vl;
int size;
enum { INITIAL_BUFFER_SIZE = 512 };
{
char buffer[INITIAL_BUFFER_SIZE];
va_start(vl, fmt);
size = vsnprintf(buffer, INITIAL_BUFFER_SIZE, fmt, vl);
va_end(vl);
if (size < INITIAL_BUFFER_SIZE)
return std::string(buffer, size);
}
size += sizeof('\0');
char buffer[size];
va_start(vl, fmt);
size = vsnprintf(buffer, size, fmt, vl);
va_end(vl);
return std::string(buffer, size);
}
(事实证明,这个版本与Piti Ongmongkolkul的答案相似,只是它没有使用new和delete[],并且在创建std::string时指定了大小。
The idea here of not using new and delete[] is to imply usage of the stack over the heap since it doesn't need to call allocation and deallocation functions, however if not properly used, it could be dangerous to buffer overflows in some (perhaps old, or perhaps just vulnerable) systems. If this is a concern, I highly suggest using new and delete[] instead. Note that the only concern here is about the allocations as vsnprintf() is already called with limits, so specifying a limit based on the size allocated on the second buffer would also prevent those.)
推荐文章
- 为什么这个结合赋值和相等检查的if语句返回true?
- cplusplus.com给出的错误、误解或坏建议是什么?
- c#:如何获得一个字符串的第一个字符?
- String类中的什么方法只返回前N个字符?
- 找出质数最快的算法是什么?
- 我可以将c#字符串值转换为转义字符串文字吗?
- 在c#中解析字符串为日期时间
- 字符串中的单词大写
- 自定义数字格式字符串始终显示符号
- c++枚举类可以有方法吗?
- string.ToLower()和string.ToLowerInvariant()
- PHP字符串中的花括号
- 如何在Python中拆分这一长行?
- 格式化IO函数(*printf / *scanf)中的转换说明符%i和%d之间的区别是什么?
- 将析构函数设为私有有什么用?