我猜对于C++比较熟悉的小伙伴,对于C++中的std::stoi、std::stol、std::stoll、std::stod、std::stof和std::to_string
都不陌生,它们共同完成字符串和数字之间的互转,使用起来非常方便,但是它们的实现大家未必真得有过研究,这不机会来了,yas已经通过非常直白的方式进行了实现,相信比大家直接去看C++源码要轻松的多,而且yas的代码也非常方便移植。
1.utoa 无符号整数转为字符串
template<typename T>
std::size_t default_traits::utoa(char *buf, const std::size_t, T v) {
std::uint64_t l = __YAS_SCAST(std::uint64_t, v), n = l;
// 计算 无符号整数 转为 字符串 需要字符个数
std::size_t len = 1;
if ( l >= 10000000000000000ull ) { len += 16; l /= 10000000000000000ull; }
if ( l >= 100000000ull ) { len += 8; l /= 100000000ull; }
if ( l >= 10000ull ) { len += 4; l /= 10000ull; }
if ( l >= 100ull ) { len += 2; l /= 100ull; }
if ( l >= 10ull ) { len += 1; }
// 设置结束符"\0"
*(buf+len) = 0;
char *p = buf+len-1;
switch ( len ) {
case 20: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 19: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 18: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 17: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 16: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 15: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 14: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 13: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 12: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 11: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 10: *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 9 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 8 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 7 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 6 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 5 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 4 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 3 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 2 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10; __YAS_FALLTHROUGH;
case 1 : *p-- = __YAS_SCAST(char, '0'+(n % 10)); n /= 10;
default: break;
}
return len;
}
代码看着很吓人,实际原理很简单:
- 计算无符号整数转为字符串所需字符数组的大小
- 设置字符串结束字符“\0”
- 将整数的每一位转为字符,存到字符数组
注意:代码中的switch case中没有break,目的就是每一位依次进行处理
2. itoa 整数转字符串
template<typename T>
std::size_t default_traits::itoa(char *buf, const std::size_t, T v) {
if ( v < 0 ) {
*buf++ = '-';
return 1 + default_traits::utoa(buf, 0/*unused*/, __YAS_SCAST(std::int64_t, std::abs(v)));
}
return default_traits::utoa(buf, 0/*unused*/, __YAS_SCAST(std::int64_t, v));
}
与utoa不同之处,就是需要考虑负号,如果,是负数,最终字符数组长度要多1;如果,是正数,直接依赖utoa的实现
3. atou 字符串转无符号整数
template<typename T>
T default_traits::atou(const char *str_, std::size_t size) {
const std::uint8_t *str = __YAS_RCAST(const std::uint8_t *, str_);
std::uint64_t v= 0;
switch ( size ) {
case 20: v = __YAS_SCAST(T, v+(str[size-20]-'0')*10000000000000000000ull); __YAS_FALLTHROUGH;
case 19: v = __YAS_SCAST(T, v+(str[size-19]-'0')*1000000000000000000ull); __YAS_FALLTHROUGH;
case 18: v = __YAS_SCAST(T, v+(str[size-18]-'0')*100000000000000000ull); __YAS_FALLTHROUGH;
case 17: v = __YAS_SCAST(T, v+(str[size-17]-'0')*10000000000000000ull); __YAS_FALLTHROUGH;
case 16: v = __YAS_SCAST(T, v+(str[size-16]-'0')*1000000000000000ull); __YAS_FALLTHROUGH;
case 15: v = __YAS_SCAST(T, v+(str[size-15]-'0')*100000000000000ull); __YAS_FALLTHROUGH;
case 14: v = __YAS_SCAST(T, v+(str[size-14]-'0')*10000000000000ull); __YAS_FALLTHROUGH;
case 13: v = __YAS_SCAST(T, v+(str[size-13]-'0')*1000000000000ull); __YAS_FALLTHROUGH;
case 12: v = __YAS_SCAST(T, v+(str[size-12]-'0')*100000000000ull); __YAS_FALLTHROUGH;
case 11: v = __YAS_SCAST(T, v+(str[size-11]-'0')*10000000000ull); __YAS_FALLTHROUGH;
case 10: v = __YAS_SCAST(T, v+(str[size-10]-'0')*1000000000ull); __YAS_FALLTHROUGH;
case 9: v = __YAS_SCAST(T, v+(str[size- 9]-'0')*100000000ull); __YAS_FALLTHROUGH;
case 8: v = __YAS_SCAST(T, v+(str[size- 8]-'0')*10000000ull); __YAS_FALLTHROUGH;
case 7: v = __YAS_SCAST(T, v+(str[size- 7]-'0')*1000000ull); __YAS_FALLTHROUGH;
case 6: v = __YAS_SCAST(T, v+(str[size- 6]-'0')*100000ull); __YAS_FALLTHROUGH;
case 5: v = __YAS_SCAST(T, v+(str[size- 5]-'0')*10000ull); __YAS_FALLTHROUGH;
case 4: v = __YAS_SCAST(T, v+(str[size- 4]-'0')*1000ull); __YAS_FALLTHROUGH;
case 3: v = __YAS_SCAST(T, v+(str[size- 3]-'0')*100ull); __YAS_FALLTHROUGH;
case 2: v = __YAS_SCAST(T, v+(str[size- 2]-'0')*10ull); __YAS_FALLTHROUGH;
case 1: v = __YAS_SCAST(T, v+(str[size- 1]-'0')*1ull);
default: break;
}
return __YAS_SCAST(T, v);
}
这里switch case中同样没有break,目的就是逐字符加权累加得到最终的无符号整数。
4. atoi 字符串转整型
template<typename T>
T default_traits::atoi(const char *str, std::size_t size) {
if ( *str == '-' ) {
++str;
return -default_traits::atou<T>(str, size-1);
}
return default_traits::atou<T>(str, size);
}
如果字符串以"-"开头,表示是一个负数的字符串,剩下的就是依赖atou将字符串转为整数
5. atod 字符串转双精度浮点型
template<typename T>
T default_traits::atod(const char *str, std::size_t size) {
static const double es[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
1e+0 ,1e+1 , 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9
,1e+10 ,1e+11 , 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19
,1e+20 ,1e+21 , 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29
,1e+30 ,1e+31 , 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39
,1e+40 ,1e+41 , 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49
,1e+50 ,1e+51 , 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59
,1e+60 ,1e+61 , 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69
,1e+70 ,1e+71 , 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79
,1e+80 ,1e+81 , 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89
,1e+90 ,1e+91 , 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99
,1e+100,1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109
,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119
,1e+120,1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129
,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139
,1e+140,1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149
,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159
,1e+160,1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169
,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179
,1e+180,1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189
,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199
,1e+200,1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209
,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219
,1e+220,1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229
,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239
,1e+240,1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249
,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259
,1e+260,1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269
,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279
,1e+280,1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289
,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299
,1e+300,1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
};
((void)size);
T v = 0.0;
bool neg = false;
if ( *str == '-' ) {
neg = true;
++str;
}
// 整数部分解析
for ( ; *str >= '0' && *str <= '9'; ++str) {
v = __YAS_SCAST(T, (v*10.0) + (*str - '0'));
}
// 小数部分解析
if ( *str == '.' ) {
double f = 0.0;
int n = 0;
++str;
for ( ; *str >= '0' && *str <= '9'; ++str, ++n) {
f = (f*10.0) + (*str - '0');
}
v += __YAS_SCAST(T, f/es[n]);
}
v = neg ? -v : v;
return v;
}
这个代码也是有点唬人,es[]这个数组也是够大的,但是,它是最容易看懂的。介绍一下代码的逻辑:
- 判断正负号
- 解析整数部分,每个循环通过x10进行加权
- 解析小数部分,每个循环通过x10进行加权,最后通过/es[n]加权
- 整数部分+小数部分
- 返回最终值
6. atof 字符串转单精度浮点型
template<typename T>
T default_traits::atof(const char *str, std::size_t size) {
return default_traits::atod<T>(str, size);
}
实现部分就一行代码,直接依赖atod,原因就是atod更具有普遍性,能做到atod,那肯定能做到atof。
7. dtoa 双精度浮点数转字符串
template<typename T>
std::size_t default_traits::dtoa(char *buf, const std::size_t size, T v) {
(void)size;
return detail::rapidjson_dtoa(v, buf) - buf;
}
哈哈,这里作者偷懒了,直接依赖了rapidjson中的实现。
8. ftoa 单精度浮点数转字符串
template<typename T>
std::size_t default_traits::ftoa(char *buf, const std::size_t bufsize, T v) {
return default_traits::dtoa(buf, bufsize, v);
}
这个没啥好说的,直接依赖dtoa就行了。
总结
看了上面的代码,相信大家对于数字与字符串之间的转换肯定有了新的认识,也不过如此是吗?我认为看起来虽然不难,但是能写出如此简洁的代码,是非常值得我们学习的。与诸君共勉!!!