字符串转数字
std::strtol & std::strtoll & std::strtoul & std::strtoull
函数原型
#include <cstdlib>
long strtol( const char *str, char **str_end, int base );
long long strtoll( const char *str, char **str_end, int base );
unsigned long strtoul( const char *str, char **str_end, int base );
unsigned long long strtoull( const char *str, char **str_end, int base );
#include <cwchar>
long wcstol( const wchar_t* str, wchar_t** str_end, int base );
long long wcstoll( const wchar_t* str, wchar_t** str_end, int base );
unsigned long wcstoul( const wchar_t* str, wchar_t** str_end, int base );
unsigned long long wcstoull( const wchar_t* str, wchar_t** str_end, int base );
说明
将 str
指向的字符串转换为整数
跳过任何空白字符,直到找到第一个非空白字符,然后获取尽可能多的有效的整数字符(基数为 base
)。有效整数字符定义如下:
- 加号(
+
)或减号(-
) - 前置
0
表示base
为8
(八进制) - 前置
0x
或0X
,表示base
为16
(十六进制) - 一系列的“数字”(对于
base
大于10
的还包括字母)
base
的有效值为 {0,2,3,…,36}。二进制的有效数字为 {0, 1},三进制的有效数字为 {0, 1, 2},以此类推。对于 base 大于 10 的情况,有效“数字”包括字母,11
进制包括 A(a)
,12
进制包括 B(b)
,…,36
进制包括 Z(z)
。
如果 base
为 0
,将自动检查进制。如果前缀是 0
,则为 8 进制,如果前缀为 0x
或 0X
,则为 16 进制,其他情况为 10 进制。
str_end
指向需要处理的字符的结尾的下一个字符,如果为空,则忽略。
如果 str
为空或者不满足转换要求,该函数将不会进行转换。
返回值
-
如果成功,则直接返回转换后的数字类型数值
-
如果返回值超出类型的范围(out of range),将会产生返回错误,设置
errno
为ERAGE
,并返回对应返回类型的最大或最小值long
:LONG_MAX
,LONG_MIN
long long
:LLONG_MAX
,LLONG_MIN
unsigned long
:ULONG_MAX
unsigned long long
:ULLONG_MAX
-
如果不能执行转换,则返回 0
代码例子
#include <cerrno>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <string>
// 打印值
template<typename T>
void print(const std::string &str, const T &value) {
std::string tmp = "std::strtol(\"" + str + "\") ";
std::cout << std::setw(30) << std::left << tmp << "= [" << value << "]";
}
void test(const std::string &str) {
errno = 0;
char *p_end{nullptr};
print(str, std::strtol(str.c_str(), &p_end, 10));
std::cout << ", p_end point to: [" << std::string(p_end) << "]";
if (errno == ERANGE) {
std::cout << ", [out of range]";
}
std::cout << '\n';
}
int main() {
test(" 12345");
test("12345 ");
test(" 12345 ");
test("12345.abc");
test("abc 12345");
test("12345123451");
test("-12345123451");
}
代码输出结果
std::strtol(" 12345") = [12345], p_end point to: []
std::strtol("12345 ") = [12345], p_end point to: [ ]
std::strtol(" 12345 ") = [12345], p_end point to: [ ]
std::strtol("12345.abc") = [12345], p_end point to: [.abc]
std::strtol("abc 12345") = [0], p_end point to: [abc 12345]
std::strtol("12345123451") = [2147483647], p_end point to: [], [out of range]
std::strtol("-12345123451") = [-2147483648], p_end point to: [], [out of range]
std::strtof & std::strtod & std::strtold
函数原型
#include <cstdlib>
float strtof( const char* str, char** str_end );
double strtod( const char* str, char** str_end );
long double strtold( const char* str, char** str_end );
#include <cwchar>
float wcstof( const wchar_t* str, wchar_t** str_end );
double wcstod( const wchar_t* str, wchar_t** str_end );
long double wcstold( const wchar_t* str, wchar_t** str_end );
说明
将 str
指向的字符串转换为浮点数
跳过任何空白字符,直到找到第一个非空白字符,然后获取尽可能多的有效的浮点数字符。浮点数字符定义如下:
-
十进制浮点表达式
- 加号(
+
)或减号(-
) - 可选包含小数点字符的十进制数字的非空序列
e
或E
后跟可选的减号(-
)或加号(+
)以及非空的十进制数字序列
- 加号(
-
十六进制浮点表达式
- 加号(
+
)或减号(-
) 0x
或0X
- 可选包含小数点字符的十六进制数字的非空序列
p
或P
后跟可选的减号(-
)或加号(+
)以及非空的十进制数字序列
- 加号(
-
无穷大表达式
- 加号(
+
)或减号(-
) INF
或INFINITY
(忽略大小写)
- 加号(
-
非数字表达式
- 加号(
+
)或减号(-
) NAN
或NAN(char_sequence)
(NAN
忽略大小写),char_sequence
只能包含数字、拉丁字母和下划线
- 加号(
str_end
指向需要处理的字符的结尾的下一个字符,如果为空,则忽略
16 进制浮点数也有指数表达方式。其指数表达形式和 10 进制的指数表达形式相似,只是将 e
换为了 p
。但是意思略有不同。
对于 16 进制浮点数指数,xPn
表示 x
的 16 进制数乘以
2
n
2^n
2n
1.0 p 1 = 1.0 × 2 1 1.0p1 = 1.0 \times 2^1 1.0p1=1.0×21
10.0 p 3 = ( 1 × 16 + 0 × 1 ) × ( 2 3 ) 10.0p3 = (1 \times 16 + 0 \times 1) \times (2^3) 10.0p3=(1×16+0×1)×(23)
12.6 p 3 = ( 1 × 16 + 2 × 1 + 6 × 1 16 ) × ( 2 3 ) = 147 12.6p3 = (1 \times 16 + 2 \times 1 + 6 \times \frac{1}{16}) \times (2^3) = 147 12.6p3=(1×16+2×1+6×161)×(23)=147
返回值
-
如果成功,则直接返回转换后的数字类型数值
-
如果返回值超出类型的范围(out of range),将会产生返回错误,设置
errno
为ERAGE
,并返回HUGE_VAL
,HUGE_VALF
或HUGE_VALL
long
:LONG_MAX
,LONG_MIN
long long
:LLONG_MAX
,LLONG_MIN
unsigned long
:ULONG_MAX
unsigned long long
:ULLONG_MAX
-
如果不能执行转换,则返回 0
代码例子
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <string>
// 打印值
template<typename T>
void print(const std::string &str, const T &value) {
std::string tmp = "std::strtof(\"" + str + "\") ";
std::cout << std::setw(30) << std::left << tmp << "= [" << value << "]";
}
void test(const std::string &str) {
errno = 0;
char *p_end{nullptr};
print(str, std::strtof(str.c_str(), &p_end));
std::cout << ", p_end point to: [" << std::string(p_end) << "]";
if (errno == ERANGE) {
std::cout << ", [out of range]";
}
std::cout << '\n';
}
int main()
{
test("123.456789123");
test("-123.456789123");
test("1.2345e3");
test("1.2345e-3");
test("-1.2345e-3");
test("0x12.6");
test("0x12.6p3");
test("nan");
test("inf");
}
代码输出结果
std::strtof("123.456789123") = [123.457], p_end point to: []
std::strtof("-123.456789123") = [-123.457], p_end point to: []
std::strtof("1.2345e3") = [1234.5], p_end point to: []
std::strtof("1.2345e-3") = [0.0012345], p_end point to: []
std::strtof("-1.2345e-3") = [-0.0012345], p_end point to: []
std::strtof("0x12.6") = [18.375], p_end point to: []
std::strtof("0x12.6p3") = [147], p_end point to: []
std::strtof("nan") = [nan], p_end point to: []
std::strtof("inf") = [inf], p_end point to: []
std::atoi & std::atol & std::atoll
函数原型
#include <cstdlib>
int atoi( const char *str);
long atol( const char *str);
long long atoll( const char *str);
说明
将 str
指向的字符串转换为整数
跳过任何空白字符,直到找到第一个非空白字符,然后获取尽可能多的有效的整数字符(基数为 base
)。有效整数字符定义如下:
- 加号(
+
)或减号(-
) - 数字
atoi 系列的函数的缺点:
- 当转换出现错误时,函数不会抛异常也不会报错
- 对于不执行转换时会返回零,对于原来字符串就是 0 是不好进行判断
返回值
- 如果成功,则直接返回转换后的数字类型数值
- 如果返回值超出类型的范围(out of range),返回值为未定义
- 如果不能执行转换,则返回 0
代码例子
#include <cstdlib>
#include <iostream>
#include <string>
#include <iomanip>
// 打印值
template<typename T>
void print(const std::string &str, const T &value) {
std::string tmp = "std::atoi(\"" + str + "\") ";
std::cout << std::setw(30) << std::left << tmp << "= [" << value << "]\n";
}
void test(const std::string& str)
{
print(str, std::atoi(str.c_str()));
}
int main()
{
test("12345");
test("123.45");
test("12345 with word");
test("words 12345");
test(" 12345");
test("12345 ");
test(" 12345 ");
test("12345123451");
test("-12345");
}
代码输出结果
std::atoi("12345") = [12345]
std::atoi("123.45") = [123]
std::atoi("12345 with word") = [12345]
std::atoi("words 12345") = [0]
std::atoi(" 12345") = [12345]
std::atoi("12345 ") = [12345]
std::atoi(" 12345 ") = [12345]
std::atoi("12345123451") = [2147483647]
std::atoi("-12345") = [-12345]
std::atof
函数原型
#include <cstdlib>
double atof(const char *str);
说明
这里 str 的定义同 std::strtof & std::strtod & std::strtold
中的定义,具体内容见上方⬆️
返回值
- 如果成功,则直接返回转换后的数字类型数值
- 如果返回值超出类型的范围(out of range),返回值为未定义
- 如果不能执行转换,则返回
0.0
代码例子
#include <cstdlib>
#include <iostream>
#include <string>
#include <iomanip>
// 打印值
template<typename T>
void print(const std::string &str, const T &value) {
std::string tmp = "std::atof(\"" + str + "\") ";
std::cout << std::setw(30) << std::left << tmp << "= [" << value << "]\n";
}
void test(const std::string str) {
print(str, std::atof(str.c_str()));
}
int main() {
test("0.000000012345");
test("123.45");
test("1.2345 with word");
test("words 1.2345");
test(" 12.345");
test("12.345 ");
test(" 12.345 ");
test("-12.345");
test("12345123451");
test("15e16");
test("0x12.6p3");
test("INF");
test("NAN");
}
代码输出结果
std::atof("0.000000012345") = [1.2345e-08]
std::atof("123.45") = [123.45]
std::atof("1.2345 with word") = [1.2345]
std::atof("words 1.2345") = [0]
std::atof(" 12.345") = [12.345]
std::atof("12.345 ") = [12.345]
std::atof(" 12.345 ") = [12.345]
std::atof("-12.345") = [-12.345]
std::atof("12345123451") = [1.23451e+10]
std::atof("15e16") = [1.5e+17]
std::atof("0x12.6p3") = [147]
std::atof("INF") = [inf]
std::atof("NAN") = [nan]
std::stoi & std::stol & std::stoll & std::stoul & std::stoull
函数原型
#include <string>
int stoi( const std::string& str, std::size_t* pos = nullptr, int base = 10 );
int stoi( const std::wstring& str, std::size_t* pos = nullptr, int base = 10 );
long stol( const std::string& str, std::size_t* pos = nullptr, int base = 10 );
long stol( const std::wstring& str, std::size_t* pos = nullptr, int base = 10 );
long long stoll( const std::string& str, std::size_t* pos = nullptr, int base = 10 );
long long stoll( const std::wstring& str, std::size_t* pos = nullptr, int base = 10 );
unsigned long stoul( const std::string& str, std::size_t* pos = nullptr, int base = 10 );
unsigned long stoul( const std::wstring& str, std::size_t* pos = nullptr, int base = 10 );
unsigned long long stoull( const std::string& str, std::size_t* pos = nullptr, int base = 10 );
unsigned long long stoull( const std::wstring& str, std::size_t* pos = nullptr, int base = 10 );
说明
stoi
系列的函数可以支持窄字符和宽字符字符串,其内部实现是调用的 strtol
系列的函数
stoi
调用std::strtol(str.c_str(), &ptr, base)
和std::wcstol(str.c_str(), &ptr, base)
stol
调用std::strtol(str.c_str(), &ptr, base)
和std::wcstol(str.c_str(), &ptr, base)
stoll
调用std::strtoll(str.c_str(), &ptr, base)
和std::wcstoll(str.c_str(), &ptr, base)
stoul
调用std::strtoul(str.c_str(), &ptr, base)
和std::wcstoul(str.c_str(), &ptr, base)
stoull
调用std::strtoull(str.c_str(), &ptr, base)
和std::wcstoull(str.c_str(), &ptr, base)
其中 str
字符串的定义和 std::strtol
系列函数中定义相同
如果 pos
不为空,字符串中第一个未处理的字符的索引将存储在 *pos
中,即 strtol
系列函数中 str_end
指向字符的索引
因为 stoi 系列的函数会抛出异常,所以在使用该系列函数式一定要注意加上 try … catch … 块
返回值
- 如果成功,则直接返回转换后的数字类型数值
- 如果失败,则会抛出异常
std::invalid_argument
: 参数无效,不执行转换时抛出该异常std::out_of_range
: 转换后超出了指定类型的范围时抛出该异常
代码例子
#include <string>
#include <iostream>
#include <iomanip>
// 打印值
template<typename T>
void print(const std::string &str, const T &value) {
std::string tmp = "std::stoi(\"" + str + "\") ";
std::cout << std::setw(30) << std::left << tmp << "= [" << value << "]";
}
void printException(const std::string &str, const std::exception& e) {
std::string tmp = "std::stoi(\"" + str + "\") ";
std::cout << std::setw(30) << std::left << tmp << "= [exception: " << e.what() << "]\n";
}
void test(const std::string& str)
{
try {
std::size_t pos{0};
print(str, std::stoi(str, &pos, 10));
std::cout << ", pos: " << pos << '\n';
} catch(const std::exception& e) {
printException(str, e);
}
}
int main() {
test("45");
test("3.14159");
test("12345 with word");
test("word 12345");
test("12345123451");
}
代码输出结果
std::stoi("45") = [45], pos: 2
std::stoi("3.14159") = [3], pos: 1
std::stoi("12345 with word") = [12345], pos: 5
std::stoi("word 12345") = [exception: invalid stoi argument]
std::stoi("12345123451") = [exception: stoi argument out of range]
std::stof & std::stod & std::stold
函数原型
#include <string>
float stof( const std::string& str, std::size_t* pos = nullptr );
float stof( const std::wstring& str, std::size_t* pos = nullptr );
double stod( const std::string& str, std::size_t* pos = nullptr );
double stod( const std::wstring& str, std::size_t* pos = nullptr );
long double stold( const std::string& str, std::size_t* pos = nullptr );
long double stold( const std::wstring& str, std::size_t* pos = nullptr );
说明
stof
系列的函数可以支持窄字符和宽字符字符串,其内部实现是调用的 strtof
系列的函数
stof
调用std::strtof(str.c_str(), &ptr)
和std::wcstof(str.c_str(), &ptr)
stod
调用std::strtod(str.c_str(), &ptr)
和std::wcstod(str.c_str(), &ptr)
stold
调用std::strtold(str.c_str(), &ptr)
和std::wcstold(str.c_str(), &ptr)
其中 str
字符串的定义和 std::strtof
系列函数中定义相同
如果 pos
不为空,字符串中第一个未处理的字符的索引将存储在 *pos
中,即 strtof
系列函数中 str_end
指向字符的索引
因为 stof 系列的函数会抛出异常,所以在使用该系列函数式一定要注意加上 try … catch … 块
返回值
- 如果成功,则直接返回转换后的数字类型数值
- 如果失败,则会抛出异常
std::invalid_argument
: 参数无效,不执行转换时抛出该异常std::out_of_range
: 转换后超出了指定类型的范围时抛出该异常
代码例子
#include <string>
#include <iostream>
#include <iomanip>
// 打印值
template<typename T>
void print(const std::string &str, const T &value) {
std::string tmp = "std::stof(\"" + str + "\") ";
std::cout << std::setw(30) << std::left << tmp << "= [" << value << "]";
}
void printException(const std::string &str, const std::exception& e) {
std::string tmp = "std::stof(\"" + str + "\") ";
std::cout << std::setw(30) << std::left << tmp << "= [exception: " << e.what() << "]\n";
}
void test(const std::string& str)
{
try {
std::size_t pos{0};
print(str, std::stof(str, &pos));
std::cout << ", pos: " << pos << '\n';
} catch(const std::exception& e) {
printException(str, e);
}
}
int main() {
test("123.456789123");
test("-123.456789123");
test("1.2345e3");
test("1.2345e-3");
test("-1.2345e-3");
test("0x12.6");
test("0x12.6p3");
}
代码输出结果
std::stof("123.456789123") = [123.457], pos: 13
std::stof("-123.456789123") = [-123.457], pos: 14
std::stof("1.2345e3") = [1234.5], pos: 8
std::stof("1.2345e-3") = [0.0012345], pos: 9
std::stof("-1.2345e-3") = [-0.0012345], pos: 10
std::stof("0x12.6") = [18.375], pos: 6
std::stof("0x12.6p3") = [147], pos: 8
使用 std::stringstream
#include <iostream>
#include <sstream>
#include <iomanip>
template <typename T>
void string_to_number(const std::string& str, T& number) {
std::stringstream ss;
ss << str;
ss >> number;
std::cout << "stringstream " << std::setw(7) << std::left << typeid(number).name() << " (" << std::quoted(str) << ") = [" << number << "]\n";
}
template <typename T>
void test(const std::string& str) {
T number;
string_to_number(str, number);
}
int main() {
test<int>("12345");
test<int>("-12345");
test<int>("12345abc");
test<int>(" 12345 abc");
test<float>("123.456789123");
test<float>("-123.456789123");
test<float>("1.2345e3");
test<float>("1.2345e-3");
test<float>("0x12.6");
test<float>("0x12.6p3");
}
代码输出结果
stringstream int ("12345") = [12345]
stringstream int ("-12345") = [-12345]
stringstream int ("12345abc") = [12345]
stringstream int (" 12345 abc") = [12345]
stringstream float ("123.456789123") = [123.457]
stringstream float ("-123.456789123") = [-123.457]
stringstream float ("1.2345e3") = [1234.5]
stringstream float ("1.2345e-3") = [0.0012345]
stringstream float ("0x12.6") = [0]
stringstream float ("0x12.6p3") = [0]
std::from_chars (c++ 17)
函数原型
#include <charconv>
struct from_chars_result {
const char* ptr;
std::errc ec;
};
enum class chars_format {
scientific = /*unspecified*/,
fixed = /*unspecified*/,
hex = /*unspecified*/,
general = fixed | scientific
};
std::from_chars_result from_chars(const char* first, const char* last,
/*see below*/& value, int base = 10);
std::from_chars_result from_chars(const char* first, const char* last, float& value,
std::chars_format fmt = std::chars_format::general);
std::from_chars_result from_chars(const char* first, const char* last, double& value,
std::chars_format fmt = std::chars_format::general);
std::from_chars_result from_chars(const char* first, const char* last, long double& value,
std::chars_format fmt = std::chars_format::general);
说明
解析满足指定模式的字符序列 [first, last)
。如果没有字符与模式匹配,或者通过解析匹配字符获得的值在值的类型中无法表示,value
不会被修改,否则,匹配模式的字符将被转换为对应的类型的值,保存在 value 中。
具体的模式如下:
-
整数解析器:与
std::strtol
在默认 (“C”) 语言环境中使用的模式相同,除了以下情况- 当
base
为16
时,0x
和0X
不能被识别 - 只能识别减号(
-
),并且仅适用于有符号整数类型的值
- 当
-
浮点数解析器:与
std::strtod
在默认 (“C”) 语言环境中使用的模式相同,除了以下情况- 在指数之外无法识别加号(仅允许在开头使用减号)
- 如果
fmt
设置了std::chars_format::scientific
但没有设置std::chars_format::fixed
,则指数部分是必需的(否则它是可选的) - 如果
fmt
设置了std::chars_format::fixed
但没有设置std::chars_format::scientific
,则不允许使用可选指数 - 如果
fmt
是std::chars_format::hex
,则不允许前缀0x
或0X
(字符串0x123
解析为值0
,未解析余数为x123
)。 - 在任何情况下,在根据 std::round_to_nearest 舍入后,结果值是最接近匹配模式的字符串值的至多两个浮点值之一。
该系列的函数不会抛出异常(non-throwing),与语言环境无关(locale-independent),不会分配内存(non-allocating)
需要注意的点:
- 不支持前置空格
- 不支持加号(
+
)开始 - 对于 16 进制的浮点数,不支持前置的
0x
和0X
- 对于浮点数,如果设置
fmt
为std::chars_format::scientific
,则字符串中必须表示为科学计数表达式(包含e
和E
部分)
返回值
- 转换成功,
std::from_chars_result
中ptr
指向与模式不匹配的第一个字符或者last
,value
为转换后的值 - 如果一个也不匹配(不能转换),
std::from_chars_result
中ptr
指向first
,ec
为std::errc::incalid_argument
,value
值保持不变 - 如果转换后数值超出了返回值类型的范围,则
std::from_chars_result
中ptr
指向first
,ec
为std::errc::result_out_of_range
,value
保持不变
性能
- On GCC it’s around
4.5x
faster thanstoi
,2.2x
faster thanatoi
and almost50x
faster thanistringstream
. - On Clang it’s around
3.5x
faster thanstoi
,2.7x
faster thanatoi
and60x
faster thanistringstream
! - MSVC performs around
3x
faster thanstoi
,~2x
faster thanatoi
and almost50x
faster thanistringstream
代码例子
#include <charconv>
#include <iostream>
#include <string>
#include <string_view>
#include <iomanip>
template<typename T>
void print(const std::string &str, const T &value) {
std::string tmp = "std::from_chars(\"" + str + "\") ";
std::cout << std::setw(35) << std::left << tmp << "= [" << value << "]";
}
template<typename T>
void test_integer(const std::string &str, int base = 10) {
T result;
auto[p, ec] = std::from_chars(str.data(), str.data() + str.size(), result, base);
if (ec == std::errc()) {
print(str, result);
std::cout << ", p point to [" << p << "]";
} else if (ec == std::errc::invalid_argument) {
print(str, "invalid argument");
std::cout << ", p point to [" << p << "]";
} else if (ec == std::errc::result_out_of_range) {
print(str, "result out of range");
std::cout << ", p point to [" << p << "]";
}
std::cout << '\n';
}
template<typename T>
void test_float(const std::string &str, std::chars_format fmt = std::chars_format::general) {
T result;
auto[p, ec] = std::from_chars(str.data(), str.data() + str.size(), result, fmt);
if (ec == std::errc()) {
print(str, result);
std::cout << ", p point to [" << p << "]";
} else if (ec == std::errc::invalid_argument) {
print(str, "invalid argument");
std::cout << ", p point to [" << p << "]";
} else if (ec == std::errc::result_out_of_range) {
print(str, "result out of range");
std::cout << ", p point to [" << p << "]";
}
std::cout << '\n';
}
int main() {
test_integer<int>("12345");
test_integer<int>(" 12345");
test_integer<int>("12345 ");
test_integer<int>(" 12345 ");
test_integer<int>("12345 with word");
test_integer<int>("word 12345");
test_integer<int>("+12345");
test_integer<int>("-12345");
test_integer<int>("12", 16);
test_integer<int>("0x12", 16);
test_float<float>("123.456789123");
test_float<float>("-123.456789123");
test_float<float>("1.2345e3");
test_float<float>("1.2345e-3");
test_float<float>("1.2345e+3");
test_float<float>("12.6", std::chars_format::hex);
test_float<float>("12.6p3", std::chars_format::hex);
}
代码输出结果
std::from_chars("12345") = [12345], p point to []
std::from_chars(" 12345") = [invalid argument], p point to [ 12345]
std::from_chars("12345 ") = [12345], p point to [ ]
std::from_chars(" 12345 ") = [invalid argument], p point to [ 12345 ]
std::from_chars("12345 with word") = [12345], p point to [ with word]
std::from_chars("word 12345") = [invalid argument], p point to [word 12345]
std::from_chars("+12345") = [invalid argument], p point to [+12345]
std::from_chars("-12345") = [-12345], p point to []
std::from_chars("12") = [18], p point to []
std::from_chars("0x12") = [0], p point to [x12]
std::from_chars("123.456789123") = [123.457], p point to []
std::from_chars("-123.456789123") = [-123.457], p point to []
std::from_chars("1.2345e3") = [1234.5], p point to []
std::from_chars("1.2345e-3") = [0.0012345], p point to []
std::from_chars("1.2345e+3") = [1234.5], p point to []
std::from_chars("12.6") = [18.375], p point to []
std::from_chars("12.6p3") = [147], p point to []
数字转字符串
std::to_string & std::to_wstring
函数原型
std::string to_string( int value );
std::string to_string( long value );
std::string to_string( long long value );
std::string to_string( unsigned value );
std::string to_string( unsigned long value );
std::string to_string( unsigned long long value );
std::string to_string( float value );
std::string to_string( double value );
std::string to_string( long double value );
说明
- 对于浮点类型
std::to_string
可能会产生意外的结果,因为返回的字符串中的有效位数可能为零 std::to_string
依赖当前语言环境进行格式化,因此从多个线程并发调用std::to_string
可能导致部分序列化
代码例子
#include <iostream>
#include <string>
#include <iomanip>
template <typename T>
void test(T value) {
std::string str = std::to_string(value);
std::cout << "std::to_string(\"" << value << "\") = [" << str << "]\n";
std::wstring wstr = std::to_wstring(value);
std::wcout << L"std::to_wstring(\"" << value << L"\") = [" << wstr << L"]\n";
}
int main() {
test(12345);
test(-12345);
test(123.45);
test(1.23e3);
test(1e-20);
test(1e30);
}
代码输出结果
std::to_string("12345") = [12345]
std::to_wstring("12345") = [12345]
std::to_string("-12345") = [-12345]
std::to_wstring("-12345") = [-12345]
std::to_string("123.45") = [123.450000]
std::to_wstring("123.45") = [123.450000]
std::to_string("1230") = [1230.000000]
std::to_wstring("1230") = [1230.000000]
std::to_string("1e-20") = [0.000000]
std::to_wstring("1e-20") = [0.000000]
std::to_string("1e+30") = [1000000000000000019884624838656.000000]
std::to_wstring("1e+30") = [1000000000000000019884624838656.000000]
sprintf & snprintf & sprintf_s & snprintf_s
函数原型
#include <cstdio>
int sprintf( char *restrict buffer, const char *restrict format, ... );
int snprintf( char *restrict buffer, size_t bufsz, const char *restrict format, ... );
int sprintf_s(char *restrict buffer, rsize_t bufsz, const char *restrict format, ...);
int snprintf_s(char *restrict buffer, rsize_t bufsz, const char *restrict format, ...);
说明
格式字符串由普通的多字节字符组成(%
除外),并按指定的格式进行转换:
转换字符 | 描述 |
---|---|
% | 格式转换标志,如果想要转换为 % ,需要使用 %% |
c | 单个字符 |
s | 字符串 |
d & i | 十进制带符号数 |
o | 八进制无符号数 |
x & X | 十六进制无符号数 |
u | 十进制无符号数 |
f & F | 十进制浮点数 |
e & E | 十进制指数浮点数 |
a & A | 十六进制浮点数 |
g & G | 十进制浮点数或十进制指数浮点数 |
- 浮点转换函数将无穷大转换为
inf
或infinity
。使用哪一个是实现定义的 - 非数字转换为
nan
或nan(char_sequence)
返回
sprintf
: 写入缓冲区的字符数(不包括终止空字符),如果发生编码错误(对于字符串和字符转换说明符),则为负值
snprintf
: 如果忽略 bufsz
,返回为将写入缓冲区的字符数(不包括终止空字符),或者如果发生编码错误(对于字符串和字符转换说明符)则为负值
sprintf_s
: 写入缓冲区的字符数,不包括空字符(只要缓冲区不是空指针并且 bufsz 不为零且不大于 RSIZE_MAX),或运行时约束违规为零,编码错误为负值
snprintf_s
: 不包括终止空字符的字符数(只要缓冲区不是空指针并且 bufsz 不为零且不大于 RSIZE_MAX,则始终写入),如果 bufsz 被忽略,它将被写入缓冲区,如果运行时约束违反或编码错误发生,则为负值
代码例子
#include <cstdio>
#include <iostream>
#include <iomanip>
int main() {
std::cout << std::setw(8) << std::left << "format" << std::setw(2) << "n" << " " << " string" << '\n';
std::cout << std::setw(20) << std::setfill('-') << '-' << std::setfill(' ')<< std::endl;
char buf[30] = {""};
int n = sprintf(buf, "%d", 12345);
std::cout << std::setw(8) << std::left << "%d:" << std::setw(2) << n << " " << buf << '\n';
n = sprintf(buf, "%d", -12345);
std::cout << std::setw(8) << std::left << "%d:" << std::setw(2) << n << " " << buf << '\n';
n = sprintf(buf, "%x", 256);
std::cout << std::setw(8) << std::left << "%x:" << std::setw(2) << n << " " << buf << '\n';
n = sprintf(buf, "%o", 256);
std::cout << std::setw(8) << std::left << "%o:" << std::setw(2) << n << " " << buf << '\n';
n = sprintf(buf, "%f", 12.345);
std::cout << std::setw(8) << std::left << "%f:" << std::setw(2) << n << " " << buf << '\n';
n = sprintf(buf, "%.4f", 12.345);
std::cout << std::setw(8) << std::left << "%.4f:" << std::setw(2) << n << " " << buf << '\n';
n = sprintf(buf, "%.4f", 12.345);
std::cout << std::setw(8) << std::left << "%.4f:" << std::setw(2) << n << " " << buf << '\n';
n = sprintf(buf, "%e", 1234.5);
std::cout << std::setw(8) << std::left << "%e:" << std::setw(2) << n << " " << buf << '\n';
n = sprintf(buf, "%.4a", 147.0);
std::cout << std::setw(8) << std::left << "%.4a:" << std::setw(2) << n << " " << buf << '\n';
}
代码输出结果
format n string
--------------------
%d: 5 12345
%d: 6 -12345
%x: 3 100
%o: 3 400
%f: 9 12.345000
%.4f: 7 12.3450
%.4f: 7 12.3450
%e: 12 1.234500e+03
%.4a: 11 0x1.2600p+7
to_chars
函数原型
#include <charconv>
struct to_chars_result {
char* ptr;
std::errc ec;
};
enum class chars_format {
scientific = /*unspecified*/,
fixed = /*unspecified*/,
hex = /*unspecified*/,
general = fixed | scientific
};
std::to_chars_result to_chars(char* first, char* last,/*see below*/ value, int base = 10);
std::to_chars_result to_chars(char*, char*, bool, int = 10) = delete;
std::to_chars_result to_chars(char* first, char* last, float value);
std::to_chars_result to_chars(char* first, char* last, double value);
std::to_chars_result to_chars(char* first, char* last, long double value);
std::to_chars_result to_chars(char* first, char* last, float value, std::chars_format fmt);
std::to_chars_result to_chars(char* first, char* last, double value, std::chars_format fmt);
std::to_chars_result to_chars(char* first, char* last, long double value, std::chars_format fmt);
std::to_chars_result to_chars(char* first, char* last, float value,
std::chars_format fmt, int precision);
std::to_chars_result to_chars(char* first, char* last, double value,
std::chars_format fmt, int precision);
std::to_chars_result to_chars(char* first, char* last, long double value,
std::chars_format fmt, int precision);
说明
将 value
值填充范围到[first, last)
指向的字符串,其中 [first, last)
必须是有效范围
返回
- 成功时,返回
to_chars_result
类型的值,其中ec
等于值初始化的std::errc
,ptr
是写入字符结尾的后一个指针。请注意,该字符串不是以NUL
结尾的 - 出错时,返回
to_chars_result
类型的值,其中ec
等于std::errc::value_too_large
代码例子
#include <iostream>
#include <charconv>
#include <string_view>
#include <iomanip>
#include <string>
template<typename T>
void print(T value, std::string_view str) {
std::cout << "std::to_chars(" << std::setw(15) << std::right << std::setprecision(12) << value << ") = [" << str << "]\n";
}
template<typename T>
void test_integer(std::string& str, T value, int base = 10) {
auto[p, ec] = std::to_chars(str.data(), str.data() + str.size(), value, base);
if (ec == std::errc()) {
print(value, std::string_view(str.data(), p - str.data()));
} else if (ec == std::errc::value_too_large) {
print(value, str);
}
}
template<typename T>
void test_float(std::string& str, T value, std::chars_format fmt = std::chars_format::general) {
auto[p, ec] = std::to_chars(str.data(), str.data() + str.size(), value, fmt);
if (ec == std::errc()) {
print(value, std::string_view(str.data(), p - str.data()));
} else if (ec == std::errc::value_too_large) {
print(value, str);
}
}
template<typename T>
void test_float_precision(std::string& str, T value, std::chars_format fmt = std::chars_format::general, int precision = 6) {
auto[p, ec] = std::to_chars(str.data(), str.data() + str.size(), value, fmt, precision);
if (ec == std::errc()) {
print(value, std::string_view(str.data(), p - str.data()));
} else if (ec == std::errc::value_too_large) {
print(value, str);
}
}
int main()
{
std::string str;
str.resize(20);
test_integer(str, 12345);
test_integer(str, -12345);
test_integer(str, 100, 8);
test_float(str, 123.456789123);
test_float_precision(str, 123.456789123, std::chars_format::general, 6);
test_float(str, -123.456789123);
test_float(str, 1234.5);
test_float(str, 18.375, std::chars_format::hex);
test_float(str, 147.0, std::chars_format::hex);
}
代码输出结果
format n string
--------------------
%d: 5 12345
%d: 6 -12345
%x: 3 100
%o: 3 400
%f: 9 12.345000
%.4f: 7 12.3450
%.4f: 7 12.3450
%e: 12 1.234500e+03
%.4a: 11 0x1.2600p+7