C++17 详解 21 — 字符串转换

本文为 《C++17 in detail》 一书的中文渣中渣译文,不足之处还望指正。

翻译太特么累人了……剩余部分还是只做摘要翻译吧。

10. 字符串转换

两类函数:

  • from_chars —将字符串转换成整数、浮点数等
  • to_chars —将数字转换成字符串

为了确保性能,这些函数有如下保证:

  • 不抛出异常——任何情况下都不抛出异常
  • 不分配内存——所有操作均是原地进行的,没有额外的内存分配
  • 不支持本地化——所有字符串均以默认 locale 解析
  • 内存安全——指明输入输出范围以允许做缓冲区安全(溢出等)检查
  • 不需要传递字符串格式
  • 对转换错误报告附加信息

用 from_chars 将字符串转换成数字

整数型适用函数:

1
2
3
4
std::from_chars_result from_chars(const char* first,
const char* last,
TYPE &value,
int base = 10);

base 取值范围 [2,36]。

浮点型适用函数:

1
2
3
4
std::from_chars_result from_chars(const char* first,
const char* last,
FLOAT_TYPE& value,
std::chars_format fmt = std::chars_format::general);

chars_format 是一个位掩码类型的枚举类:

1
2
3
4
5
6
enum class chars_format {
scientific = /*unspecified*/,
fixed = /*unspecified*/,
hex = /*unspecified*/,
general = fixed | scientific
};

所有 from_chars 函数均返回 from_chars_result 类型:

1
2
3
4
struct from_chars_result {
const char* ptr;
std::errc ec;
};
  • 转换成功时,ptr 指向第一个不匹配字符,如果所有字符全匹配则跟 last 指向同一位置;ec 执行值初始化。
  • 转换无效时,ptr = first;ec = std::errc::invalid_argument;不修改 value。
  • 超出范围,即数字大小超出 value 类型范围时,ec = std::errc::result_out_of_range;ptr 指向第一个不匹配字符;不修改 value。

用 to_chars 将数字转换成字符串

整型适用函数:

1
2
std::to_chars_result to_chars(char* first, char* last,
TYPE value, int base = 10);

浮点型适用函数:

1
2
3
4
5
6
7
8
9
10
std::to_chars_result to_chars(char* first, char* last, FLOAT_TYPE value);

std::to_chars_result to_chars(char* first, char* last,
FLOAT_TYPE value,
std::chars_format fmt);

std::to_chars_result to_chars(char* first, char* last,
FLOAT_TYPE value,
std::chars_format fmt,
int precision);

所有函数都返回 std::to_chars_result 类型:

1
2
3
4
struct to_chars_result {
char* ptr;
std::errc ec;
};
  • 成功时,ec 进行值初始化;ptr 指向最后一个字符后一个位置。注意字符串不是以空字符结束的。
  • 出错时,ptr = first;ec = std::errc::invalid_argument。
  • 超出范围时,ec = std::errc::value_too_large;[first, last) 范围变成未知状态。

Benchmark

作者实现了如下一种测试条件:

  • 随机数生成 vector

  • vector 转换成 vector,再把 vector 转换回 vector,分别测量两个阶段的耗时

  • 分别测试如下转换接口:

    — from_chars/to_chars

    — to_string/stoi

    — sprintf/atoi

    — ostringstream/istringstream

完整的测试代码可在这里找到:https://www.cppindetail.com/data/cpp17indetail.zip —— “Chapter String Conversions/conversion_benchmark.cpp”

结果:

函数 GCC 8.2 Clang 7.0 Win VS 2017 15.8 x64
to_chars 21.94 18.15 24.81
from_chars 15.96 12.74 13.43
to_string 61.84 16.62 20.91
stoi 70.81 45.75 42.40
sprintf 56.85 124.72 131.03
atoi 35.90 34.81 32.50
ostringstream 264.29 681.29 575.95
stringstream 306.17 789.04 664.90

译注:

可惜没有做浮点型数据的测试。我早先(C++17 之前)在实现日志库时曾对标准库提供的这些转换函数做过类似的性能对比,的确如上表所列:同一函数不同编译器实现的性能差距很大!而同一函数对整型和浮点型的表现差异又天差地别。
希望这个问题在 to_chars 和 from_chars 上已经不存在了。

不过如函数原型所示,结果是以传出参数形式给出的,不利于链式调用。还是继续期待 fmt 库吧。

评论