#новичкам
С++17 нам принес новую прекрасную функцию парсинга строк в числа - std::from_char.
std::from_chars_result from_chars(
const char* first, // Начало строки (включительно)
const char* last, // Конец строки (не включительно)
IntegerType& value, // Куда записать результат
int base = 10 // Система счисления (2-36)
);
std::from_chars_result from_chars(
const char* first, // Начало строки (включительно)
const char* last, // Конец строки (не включительно)
FloatType& value, // Куда записать результат
std::chars_format fmt = std::chars_format::general // Формат плавающей точки
);
На самом деле это два семейства перегрузок функций для целых чисел и чисел с плавающей точкой.
Задача функции - максимально быстро, безо всяких накладных расходов на выделение памяти и поддержку исключений, распарсить строку в число арифметического типа.
Функция возвращает структуру
std::from_chars_result:struct from_chars_result {
const char* ptr; // Указатель на первый НЕпрочитанный символ
std::errc ec; // Код ошибки (если успех — std::errc())
};Если парсинг удался и какая-то часть строки конвертировалась в число, то в ptr находится указатель на первый символ, на котором парсинг завершился. Если вся строка была интерпретирована, как число, то в ptr находится last указатель.
Если парсинг неудался, то ptr равен first, а код ошибки ec выставляется в std::errc::invalid_argument.
"123" → удачно распарсили все → ptr == last (конец строки).
"123abc" → распарсили "123" → ptr указывает на 'a'.
"abc" → ошибка → ptr == first (начало строки).
Примеры работы:
const std::string str = "42abc";
int value;
auto res = std::from_chars(str.data(), str.data() + str.size(), value);
if (res.ec == std::errc()) {
std::cout << "Value: " << value << "\n"; // 42
std::cout << "Remaining: " << res.ptr << "\n"; // "abc"
}
// ----------------
const std::string str = "xyz";
int value;
auto res = std::from_chars(str.data(), str.data() + str.size(), value);
assert(res.ec == std::errc::invalid_argument);
assert(res.ptr == str.data()); // ptr остался на начале
К тому же функция может детектировать переполнение:
const std::string str = "99999999999999999999";
int value;
auto res = std::from_chars(str.data(), str.data() + str.size(), value);
assert(res.ec == std::errc::result_out_of_range);
В чем главный прикол этой функции?
Помимо отсутствия накладных расходов, это последовательный парсинг. Если у вас есть строка с последовательностью чисел, разделенных запятой, то вы просто в цикле можете передвигать нужные указатели и парсить числа одно за другим. Тот же std::stoi выкинул бы исключение и пошел пиво пить:
const std::string str = "123,456,789";
std::vector<int> numbers;
const char* current = str.data();
const char* end = str.data() + str.size();
while (current < end) {
int value;
auto res = std::from_chars(current, end, value);
if (res.ec != std::errc()) {
std::cerr << "Parsing error!\n";
break;
}
numbers.emplace_back(value);
current = res.ptr; // Сдвигаем указатель
// Пропускаем разделитель (запятую)
if (current < end && *current == ',') {
++current;
}
}
for (int num : numbers) {
std::cout << num << " ";
}
// Вывод: 123 456 789
К тому же ее целочисленный вариант с С++23 constexpr, что позволить вам парсить строку в числа даже во время компиляции.
Если вы не любите исключения - std::from_char ваш выбор.
Be efficient. Stay cool.
#cpp17 #cpp23