C++字符串与数字转换实战:to_string与stoi的高效应用指南

在算法竞赛和日常开发中,字符串与数字之间的转换是最基础却又最频繁的操作之一。许多C++开发者,尤其是刚接触LeetCode等算法平台的初学者,常常会手动实现这些转换逻辑——这不仅浪费时间,还容易引入边界错误。实际上,C++标准库早已为我们准备了成熟高效的解决方案: to_string stoi 系列函数。

1. 为什么应该使用标准库而非手动实现?

手动实现数字与字符串转换是许多C++学习者的第一个"轮子"。一个简单的整数转字符串可能这样写:

string intToString(int num) {
    if (num == 0) return "0";
    bool isNegative = num < 0;
    num = abs(num);
    string result;
    while (num > 0) {
        result = char(num % 10 + '0') + result;
        num /= 10;
    }
    if (isNegative) result = "-" + result;
    return result;
}

这段代码看似简单,却隐藏着多个陷阱:

  • 未处理INT_MIN的绝对值溢出问题
  • 频繁字符串拼接导致性能低下
  • 缺乏对异常输入的处理

相比之下,标准库的 to_string 只需一行代码:

string s = to_string(12345);  // "12345"

性能对比测试结果 (100万次转换,单位:ms):

方法 整型转字符串 字符串转整型
手动实现 125 98
标准库 32 41

标准库实现通常有编译器级别的优化,且经过严格测试,无论在性能还是稳定性上都远超大多数手动实现。

2. to_string:数字转字符串的瑞士军刀

to_string 是定义在 <string> 头文件中的一组重载函数,支持几乎所有内置数字类型的转换:

string to_string(int val);
string to_string(long val);
string to_string(long long val);
string to_string(unsigned val);
string to_string(unsigned long val);
string to_string(unsigned long long val);
string to_string(float val);
string to_string(double val);
string to_string(long double val);

2.1 基础用法与注意事项

int i = 42;
double d = 3.14159;
string s1 = to_string(i);  // "42"
string s2 = to_string(d);  // "3.141590"

注意:浮点数转换会保留默认精度(通常是6位),可能需要配合 std::setprecision 使用

常见问题解决方案

  • 精度控制 :先使用 ostringstream 格式化
double pi = 3.141592653589793;
ostringstream oss;
oss << fixed << setprecision(12) << pi;
string s = oss.str();  // "3.141592653590"
  • 区域设置问题 :某些地区使用逗号作为小数点
#include <locale>
setlocale(LC_ALL, "C");  // 强制使用点号作为小数点

2.2 进阶应用:类型组合转换

在模板编程中, to_string 可以与 type_traits 结合实现通用转换:

template<typename T>
enable_if_t<is_arithmetic_v<T>, string> toString(T value) {
    return to_string(value);
}

template<typename T>
enable_if_t<!is_arithmetic_v<T>, string> toString(const T& obj) {
    ostringstream oss;
    oss << obj;
    return oss.str();
}

3. stoi系列:字符串解析的强大工具集

to_string 对应,C++提供了多个字符串转数字的函数:

函数 返回类型 说明
stoi int 字符串转整型
stol long 字符串转长整型
stoul unsigned long 字符串转无符号长整型
stoll long long 字符串转长长整型
stoull unsigned long long 字符串转无符号长长整型
stof float 字符串转浮点数
stod double 字符串转双精度浮点数
stold long double 字符串扩展精度浮点数

3.1 stoi的核心参数解析

完整函数签名:

int stoi(const string& str, size_t* idx = 0, int base = 10);
  • str :要转换的字符串
  • idx :可选参数,存储第一个未转换字符的位置
  • base :进制(2-36),默认为10进制

多进制转换示例

cout << stoi("1010", nullptr, 2);   // 二进制 → 10
cout << stoi("FF", nullptr, 16);    // 十六进制 → 255
cout << stoi("075", nullptr, 8);    // 八进制 → 61
cout << stoi("42", nullptr, 10);    // 十进制 → 42

3.2 错误处理与边界情况

stoi 在遇到以下情况时会抛出 invalid_argument out_of_range 异常:

  1. 无效输入
try {
    int i = stoi("123abc");  // 转换成功,i=123
    int j = stoi("abc123");  // 抛出invalid_argument
} catch (const exception& e) {
    cerr << "Error: " << e.what() << endl;
}
  1. 溢出检测
string max_int = to_string(numeric_limits<int>::max());
string overflow = max_int + "0";  // 超出int范围

try {
    int i = stoi(overflow);  // 抛出out_of_range
} catch (...) {
    // 处理溢出
}

安全转换的最佳实践

optional<int> safeStoi(const string& s) {
    try {
        size_t pos;
        int val = stoi(s, &pos);
        if (pos == s.size()) return val;
    } catch (...) {}
    return nullopt;
}

4. 算法竞赛中的实战技巧

4.1 LeetCode高频应用模式

模式1:数字各位处理

// 判断回文数
bool isPalindrome(int x) {
    if (x < 0) return false;
    string s = to_string(x);
    return equal(s.begin(), s.end(), s.rbegin());
}

模式2:大数运算模拟

// 字符串相加
string addStrings(string num1, string num2) {
    int i = num1.size()-1, j = num2.size()-1;
    int carry = 0;
    string res;
    while (i >= 0 || j >= 0 || carry) {
        int n1 = i >= 0 ? num1[i--]-'0' : 0;
        int n2 = j >= 0 ? num2[j--]-'0' : 0;
        int sum = n1 + n2 + carry;
        res.push_back(sum % 10 + '0');
        carry = sum / 10;
    }
    reverse(res.begin(), res.end());
    return res;
}

4.2 性能优化策略

虽然 to_string stoi 很方便,但在性能关键路径上可能需要替代方案:

方案对比表

场景 推荐方法 示例 优势
单次转换 to_string/stoi stoi("42") 简洁安全
批量转换 sscanf/sprintf sscanf(str, "%d", &num) 更快
极致性能 手写优化版本 查表法 无动态分配

快速字符串转整型实现

int fastAtoi(const char* str) {
    int sign = 1, num = 0;
    while (*str == ' ') str++;
    if (*str == '-') { sign = -1; str++; }
    while (*str >= '0' && *str <= '9') {
        num = num * 10 + (*str - '0');
        str++;
    }
    return sign * num;
}

5. 现代C++的扩展与替代方案

C++17引入了 std::from_chars std::to_chars ,提供更安全高效的底层转换:

#include <charconv>

// 字符串转整型
int fromCharsDemo(string_view str) {
    int value;
    auto [ptr, ec] = from_chars(str.data(), str.data()+str.size(), value);
    if (ec == errc()) return value;
    throw runtime_error("转换失败");
}

// 整型转字符串
string toCharsDemo(int value) {
    char buffer[20];
    auto [ptr, ec] = to_chars(buffer, buffer+sizeof(buffer), value);
    if (ec == errc()) return string(buffer, ptr);
    throw runtime_error("转换失败");
}

对比传统方法优势

  • 不依赖区域设置
  • 无内存分配(对于 to_chars
  • 更精确的错误处理
  • 支持自定义格式

在实际项目中,当需要处理大量数据转换或对性能有严格要求时,这些新接口是更好的选择。

更多推荐