C++ 如何以优雅高效的方式将无符号/有符号整数/长字符串转换为C字符串?
这里有很多人问过如何将无符号/有符号整数/长字符串转换为C字符串 最常见的答案是使用sprintf(或snprintf)。但是,它需要不同类型的不同格式字符串(例如uint32\u t、int32\u t、uint64\u t、int64\u t等) 我有这样一个函数模板:C++ 如何以优雅高效的方式将无符号/有符号整数/长字符串转换为C字符串?,c++,c++11,g++,C++,C++11,G++,这里有很多人问过如何将无符号/有符号整数/长字符串转换为C字符串 最常见的答案是使用sprintf(或snprintf)。但是,它需要不同类型的不同格式字符串(例如uint32\u t、int32\u t、uint64\u t、int64\u t等) 我有这样一个函数模板: // T can only be uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t template <type T> void foo(char*
// T can only be uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t
template <type T>
void foo(char* buffer, T value) {
// write value to buffer
}
//T只能是uint16\u T、int16\u T、uint32\u T、uint64\u T
模板
void foo(字符*缓冲区,T值){
//将值写入缓冲区
}
我可以专门化函数模板来解决我的问题
我只是想知道是否有一种更优雅、更高效的方法(即没有像stringstream这样的临时缓冲区)
谢谢 C++17之前最简单的实现可能是:
std::strcpy(buffer, std::to_string(value).c_str());
这确实需要一个临时缓冲区(一个临时的std::string
),但我会犹豫是否过早地进行优化。在我看来,这将是最优雅的转换方式——它简单易懂
(请注意,函数签名不可能确保缓冲区
指针指向足以容纳字符串化值的分配。)
在C++17中,您可以直接使用(您必须自己使用此函数添加null终止字符;文档中没有说明它为您添加了一个)
也许有一个中间地带,您可以声明一个trait来获取每个数字类型的printf样式格式说明符
#include <cstdio>
template <typename T>
struct printf_specifier
{
static char const * const value;
};
template<> char const * const printf_specifier<char>::value = "%hhd";
template<> char const * const printf_specifier<unsigned char>::value = "%hhu";
template<> char const * const printf_specifier<short>::value = "%hd";
template<> char const * const printf_specifier<unsigned short>::value = "%hu";
template<> char const * const printf_specifier<int>::value = "%d";
template<> char const * const printf_specifier<unsigned int>::value = "%u";
template<> char const * const printf_specifier<long>::value = "%ld";
template<> char const * const printf_specifier<unsigned long>::value = "%lu";
template<> char const * const printf_specifier<long long>::value = "%lld";
template<> char const * const printf_specifier<unsigned long long>::value = "%llu";
template<> char const * const printf_specifier<float>::value = "%f";
template<> char const * const printf_specifier<double>::value = "%f";
template <typename T>
void foo(char *buffer, T value)
{
std::sprintf(buffer, printf_specifier<T>::value, value);
}
如果即使这样也太膨胀,您也可以完全自己进行转换:
#include <algorithm>
#include <cstdlib>
template <typename T>
void foo(char *buffer, T value)
{
static_assert(std::is_integral<T>::value, "Type of value must be an integral type");
if (value < 0) {
*(buffer++) = '-';
}
char *start = buffer;
while (value != 0) {
*(buffer++) = '0' + std::abs(value % 10);
value /= 10;
}
if (buffer == start) {
*(buffer++) = '0';
} else {
std::reverse(start, buffer);
}
*buffer = '\0';
}
#包括
#包括
模板
void foo(字符*缓冲区,T值)
{
静态_断言(std::is_integral::value,“值的类型必须是整数类型”);
如果(值<0){
*(buffer++)='-';
}
char*start=buffer;
while(值!=0){
*(buffer++)=“0”+std::abs(值%10);
数值/=10;
}
如果(缓冲区==开始){
*(缓冲区++)=“0”;
}否则{
标准::反向(启动、缓冲);
}
*缓冲区='\0';
}
使用log10计算字符串的长度并从后向前写入可能会更快,而不是向后写入然后反转,但是如果您认为有必要,我将把这个选项留给您作为练习。也许C++17之前最简单的实现是:
std::strcpy(buffer, std::to_string(value).c_str());
这确实需要一个临时缓冲区(一个临时的std::string
),但我会犹豫是否过早地进行优化。在我看来,这将是最优雅的转换方式——它简单易懂
(请注意,函数签名不可能确保缓冲区
指针指向足以容纳字符串化值的分配。)
在C++17中,您可以直接使用(您必须自己使用此函数添加null终止字符;文档中没有说明它为您添加了一个)
也许有一个中间地带,您可以声明一个trait来获取每个数字类型的printf样式格式说明符
#include <cstdio>
template <typename T>
struct printf_specifier
{
static char const * const value;
};
template<> char const * const printf_specifier<char>::value = "%hhd";
template<> char const * const printf_specifier<unsigned char>::value = "%hhu";
template<> char const * const printf_specifier<short>::value = "%hd";
template<> char const * const printf_specifier<unsigned short>::value = "%hu";
template<> char const * const printf_specifier<int>::value = "%d";
template<> char const * const printf_specifier<unsigned int>::value = "%u";
template<> char const * const printf_specifier<long>::value = "%ld";
template<> char const * const printf_specifier<unsigned long>::value = "%lu";
template<> char const * const printf_specifier<long long>::value = "%lld";
template<> char const * const printf_specifier<unsigned long long>::value = "%llu";
template<> char const * const printf_specifier<float>::value = "%f";
template<> char const * const printf_specifier<double>::value = "%f";
template <typename T>
void foo(char *buffer, T value)
{
std::sprintf(buffer, printf_specifier<T>::value, value);
}
如果即使这样也太膨胀,您也可以完全自己进行转换:
#include <algorithm>
#include <cstdlib>
template <typename T>
void foo(char *buffer, T value)
{
static_assert(std::is_integral<T>::value, "Type of value must be an integral type");
if (value < 0) {
*(buffer++) = '-';
}
char *start = buffer;
while (value != 0) {
*(buffer++) = '0' + std::abs(value % 10);
value /= 10;
}
if (buffer == start) {
*(buffer++) = '0';
} else {
std::reverse(start, buffer);
}
*buffer = '\0';
}
#包括
#包括
模板
void foo(字符*缓冲区,T值)
{
静态_断言(std::is_integral::value,“值的类型必须是整数类型”);
如果(值<0){
*(buffer++)='-';
}
char*start=buffer;
while(值!=0){
*(buffer++)=“0”+std::abs(值%10);
数值/=10;
}
如果(缓冲区==开始){
*(缓冲区++)=“0”;
}否则{
标准::反向(启动、缓冲);
}
*缓冲区='\0';
}
使用log10计算字符串的长度并从后向前写入可能会更快,而不是将其向后写入然后反转,但如果您认为有必要,我会将该选项留给您作为练习。只需使用或:
并将结果复制到新的std::string
这里有一些可以很好地优化的东西。与常规itoa不同的是,它的整数除法减少了两倍,而在大多数CPU上,整数除法并不是简单的指令
static int log10_1(unsigned int num)
{
int ret;
static_assert(sizeof(num) == 4, "expected 32-bit unsigned int");
// extend this logic for 64 bit numbers
if (num >= 10000)
{
if (num >= 1000000)
{
if (num >= 100000000)
ret = (num >= 1000000000) ? 10 : 9;
else
ret = (num >= 10000000) ? 8 : 7;
}
else
ret = (num >= 100000) ? 6 : 5;
}
else
{
if (num >= 100)
ret = num >= 1000 ? 4 : 3;
else
ret = num >= 10 ? 2 : 1;
}
return ret;
}
// write string representation of number `n` into buf and return pointer to rterminating null
char* to_str(char* buf, unsigned int n)
{
static const char dig_[] = "0001020304050607080910111213141516171819"
"20212223242526272829303132333435363738394041424344454647484950515253545556575859"
"60616263646566676869707172737475767778798081828384858687888990919293949596979899";
int len = log10_1(n);
char *p = buf + len;
*p-- = 0;
while (n >= 100)
{
unsigned int x = (n % 100) * 2;
n /= 100;
*p-- = dig_[x + 1];
*p-- = dig_[x];
}
if (n >= 10)
{
unsigned int x = n * 2;
*p-- = dig_[x + 1];
*p-- = dig_[x];
}
else
*p-- = (char)('0' + n);
return buf + len;
}
// write string representation of number `n` into buf and return pointer to terminating null
char* to_str(char* buf, int n)
{
unsigned int l;
if (n < 0)
{
*buf++ = '-';
if (n == INT_MIN)
{
static_assert(sizeof(n) == 4, "expected 32-bit int");
memcpy(buf, "2147483648", 10);
return buf + 10;
}
l = (unsigned int)(-n);
}
else
l = (unsigned int)n;
return to_str(buf, l);
}
对于优化到的字符串函数,已经有一个良好的stackoverflow页:。最快的算法与我的基本相同。只需使用或:
并将结果复制到新的std::string
这里有一些可以很好地优化的东西。与常规itoa不同的是,它的整数除法减少了两倍,而在大多数CPU上,整数除法并不是简单的指令
static int log10_1(unsigned int num)
{
int ret;
static_assert(sizeof(num) == 4, "expected 32-bit unsigned int");
// extend this logic for 64 bit numbers
if (num >= 10000)
{
if (num >= 1000000)
{
if (num >= 100000000)
ret = (num >= 1000000000) ? 10 : 9;
else
ret = (num >= 10000000) ? 8 : 7;
}
else
ret = (num >= 100000) ? 6 : 5;
}
else
{
if (num >= 100)
ret = num >= 1000 ? 4 : 3;
else
ret = num >= 10 ? 2 : 1;
}
return ret;
}
// write string representation of number `n` into buf and return pointer to rterminating null
char* to_str(char* buf, unsigned int n)
{
static const char dig_[] = "0001020304050607080910111213141516171819"
"20212223242526272829303132333435363738394041424344454647484950515253545556575859"
"60616263646566676869707172737475767778798081828384858687888990919293949596979899";
int len = log10_1(n);
char *p = buf + len;
*p-- = 0;
while (n >= 100)
{
unsigned int x = (n % 100) * 2;
n /= 100;
*p-- = dig_[x + 1];
*p-- = dig_[x];
}
if (n >= 10)
{
unsigned int x = n * 2;
*p-- = dig_[x + 1];
*p-- = dig_[x];
}
else
*p-- = (char)('0' + n);
return buf + len;
}
// write string representation of number `n` into buf and return pointer to terminating null
char* to_str(char* buf, int n)
{
unsigned int l;
if (n < 0)
{
*buf++ = '-';
if (n == INT_MIN)
{
static_assert(sizeof(n) == 4, "expected 32-bit int");
memcpy(buf, "2147483648", 10);
return buf + 10;
}
l = (unsigned int)(-n);
}
else
l = (unsigned int)n;
return to_str(buf, l);
}
对于优化到的字符串函数,已经有一个良好的stackoverflow页:。最快的算法与我的基本相同。感谢您的快速回复。同意避免所有过早的优化。但它在我的应用程序的关键路径上(要求非常快…<2微秒…,所以一切都很重要)。因此,我想看看其他人将如何实现:)不幸的是,我仅限于GCC 4.8.x,它甚至没有实现完整的C++11(尽管我在这里放了一个C++11标记)。@Hei我添加了一种可能对您有用的方法。@cdhowie这就是MS std::to_字符串的实现方式(AFAIK)@嗨,如果你担心你的关键路径,我建议你避免使用snprintf/printf。它将大量代码加载到指令缓存中,这会影响延迟。如果您的应用程序对损失100纳秒很敏感,您可能需要测量并比较自己的时间。特别是如果你只需要转换整数。谢谢你的快速回复。同意避免所有过早的优化。但它在我的应用程序的关键路径上(要求非常快…<2微秒…,所以一切都很重要)。所以我想看看其他人将如何实现:)不幸的是,我仅限于GCC4.8.x,它甚至没有实现完整的C++11(尽管