Avatar ·

Как преобразовать std::string в int в C++ (без потери данных)?

Всем привет! Я новичок в C++ и пишу небольшую программу для учёта товаров. Данные приходят из текстового файла, где цена товара хранится в виде строки (например, "1500"). Мне нужно выполнить арифметические операции над этими числами: сложить цены, вычислить скидку и т. д. Но компилятор выдаёт ошибку, когда я пытаюсь сложить две строки. Вот мой пример кода: ```cpp #include #include int main() { std::string price1 = "2500"; std::string price2 = "1800"; int total = price1 + price2; // Ошибка! std::cout << "Total: " << total << std::endl; return 0; } ``` Я понимаю, что строки нельзя складывать как числа, но как правильно преобразовать std::string в int? Нужно, чтобы программа работала без сбоев, даже если в строке окажется мусор (например, "1500abc") или нечисловые символы. Пробовал atoi(), но читал, что это устаревший и опасный способ. Подскажите, пожалуйста, современные и безопасные методы конвертации в 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;
}