Как преобразовать std::string в int в C++ (без потери данных)?
Использование std::stoi (C++11 и новее)
Начиная с C++11, стандартная библиотека предоставляет функцию std::stoi (string to int), которая является безопасной и современной альтернативой устаревшей atoi или sscanf. Она выбрасывает исключения при ошибках, что позволяет контролировать некорректный ввод.
Базовый пример
#include <iostream>
#include <string>
int main() {
std::string price1 = "2500";
std::string price2 = "1800";
int num1 = std::stoi(price1);
int num2 = std::stoi(price2);
int total = num1 + num2;
std::cout << "Total: " << total << std::endl; // Вывод: Total: 4300
return 0;
}
Обработка ошибок
std::stoi генерирует два типа исключений:
std::invalid_argument— если строка не содержит числа (например, "abc" или пустая строка).std::out_of_range— если число выходит за пределы типа int (например, "999999999999").
Пример с обработкой ошибок:
#include <iostream>
#include <string>
#include <stdexcept>
int safeStringToInt(const std::string& str) {
try {
// size_t* idx — необязательный параметр для получения индекса первого неконвертированного символа
size_t pos;
int result = std::stoi(str, &pos);
// Проверка, что после числа нет лишних символов (например, "1500abc")
if (pos != str.length()) {
throw std::invalid_argument("Строка содержит нечисловые символы после числа");
}
return result;
} catch (const std::invalid_argument& e) {
std::cerr << "Ошибка: '" << str << "' не является числом. " << e.what() << std::endl;
return 0; // Или другое значение по умолчанию
} catch (const std::out_of_range& e) {
std::cerr << "Ошибка: число '" << str << "' выходит за пределы int. " << e.what() << std::endl;
return 0;
}
}
int main() {
std::string valid = "2500";
std::string invalid1 = "abc";
std::string invalid2 = "1500abc";
std::string overflow = "999999999999";
std::cout << safeStringToInt(valid) << std::endl; // 2500
std::cout << safeStringToInt(invalid1) << std::endl; // 0 с сообщением об ошибке
std::cout << safeStringToInt(invalid2) << std::endl; // 0 с сообщением об ошибке
std::cout << safeStringToInt(overflow) << std::endl; // 0 с сообщением об ошибке
return 0;
}
Альтернатива: std::from_chars (C++17)
Для максимальной производительности (например, в высоконагруженных системах) используйте std::from_chars из заголовка <charconv>. Он не выбрасывает исключений и не выделяет память:
#include <iostream>
#include <string>
#include <charconv>
#include <system_error>
int main() {
std::string str = "2500";
int value;
auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value);
if (ec == std::errc()) {
std::cout << "Успех: " << value << std::endl;
} else if (ec == std::errc::invalid_argument) {
std::cerr << "Не число" << std::endl;
} else if (ec == std::errc::result_out_of_range) {
std::cerr << "Выход за пределы int" << std::endl;
}
return 0;
}
Рекомендации
- Для обычных задач используйте
std::stoiс обработкой исключений — это просто и читаемо. - Для критичного по производительности кода (парсинг миллионов строк) выбирайте
std::from_chars. - Избегайте
atoiиsscanf: они не сообщают об ошибках и приводят к неопределённому поведению. - Всегда проверяйте, что строка содержит только цифры (и, возможно, знак минуса), если это критично для вашей логики.
Использование std::stringstream
Классический способ, который работает во всех версиях C++ (даже до C++11). Он менее производительный, чем std::stoi, но может быть полезен, если вы уже используете потоки ввода-вывода.
#include <iostream>
#include <sstream>
#include <string>
int main() {
std::string price = "2500";
int number;
std::stringstream ss(price);
ss >> number;
if (ss.fail()) {
std::cerr << "Ошибка преобразования!" << std::endl;
} else {
std::cout << "Число: " << number << std::endl;
}
return 0;
}
Плюсы:
- Работает в старых стандартах C++.
- Можно гибко настраивать формат (например, шестнадцатеричные числа).
Минусы:
- Медленнее, чем
std::stoi(создаётся объект потока). - Сложнее обрабатывать частичный ввод (например, "1500abc" — поток прочитает 1500 и установит флаг
fail).
Если вам нужно проверить, что вся строка состоит из числа, используйте дополнительную проверку:
#include <iostream>
#include <sstream>
#include <string>
bool isWholeStringInt(const std::string& str, int& out) {
std::stringstream ss(str);
ss >> out;
// Проверяем, что поток не в состоянии ошибки и достиг конца строки
return !ss.fail() && ss.eof();
}
int main() {
int value;
if (isWholeStringInt("2500", value)) {
std::cout << "OK: " << value << std::endl;
}
if (!isWholeStringInt("1500abc", value)) {
std::cout << "Невалидная строка" << std::endl;
}
return 0;
}