Menu

Строковые потоки в C++: разбор и сборка с помощью stringstream

Как использовать std::stringstream, istringstream и ostringstream для разбора текста, разбиения строк по пробелам, преобразования между строками и числами и сборки форматированных строк в памяти.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Поток, живущий в строке

Вы уже знаете cin и cout - потоки, подключённые к клавиатуре и экрану. Строковый поток - это та же идея, только данные живут вместо этого в std::string в памяти. Вы пишете в него с помощью << и извлекаете значения с помощью >>, ровно так же, как с консольными потоками, но ничто не касается терминала.

Эта единственная идея решает две повседневные задачи: разбор текста на типизированные значения и сборку форматированной строки из разнородных частей. Всё находится в заголовке <sstream> и существует в трёх вариантах:

  • istringstream - только для чтения, для разбора ввода.
  • ostringstream - только для записи, для сборки вывода.
  • stringstream - в обоих направлениях.

ss.str() возвращает всё написанное на данный момент как обычную string. Операторы << выполнили то же форматирование int в текст, что делает cout - вы просто захватили результат вместо того, чтобы напечатать его.

Разбор: извлечение типизированных значений

Настоящая мощь проявляется с >>. Подайте потоку некоторый текст, и извлечение преобразует каждый токен в нужный вам тип, пропуская пробелы между ними. Это чистый способ разбить одну строку на несколько типизированных полей:

Каждый >> читает до следующего пробела и преобразует: "Ada" в string, "36" в int, "5.5" в double. Обратите внимание на istringstream здесь - тип только для ввода ясно показывает, что вы лишь разбираете.

Преобразование строк в числа (безопасно)

Частая задача - превратить одну строку вроде "42" в int. stringstream делает это и сообщает, было ли текстом действительно корректное число - чего atoi никогда не делает:

Здесь ss >> value читает 123, останавливается на a и завершается успешно - поэтому всегда проверяйте всю строку, если это важно. Надёжная проверка - прочитать число, а затем убедиться, что не осталось ничего значимого. Для простых преобразований одиночного значения std::stoi, std::stod и им подобные (рассмотрены вместе с приведением типов) короче; берите stringstream, когда одна строка несёт несколько разнотипных значений.

Разбиение строки по разделителю

Разбиение текста - самая нагугленная задача со строковыми потоками. Скомбинируйте поток с std::getline, который может читать до любого символа-разделителя, а не только до перевода строки:

Шаблон while (getline(...)) повторяется, пока поток не иссякнет, потому что getline возвращает поток, а поток вычисляется как false, когда ничего не осталось. Уберите третий аргумент - и вы вместо этого разобьёте по токенам, разделённым пробелами, с помощью >>; выбирайте тот разделитель, который использует ваш формат данных.

Сборка строк в памяти

В обратном направлении ostringstream собирает форматированную строку без риска переполнения буфера, как у sprintf, и без хрупкой ручной конкатенации. Он идеален для строк журнала, имён файлов или сообщений, сшитых из чисел и текста:

Всё форматирование из <iomanip>, которое вы использовали бы с cout - setw, setfill, setprecision, hex - работает на строковом потоке одинаково, так что вы получаете дополненный, фиксированной ширины или шестнадцатеричный вывод, захваченный прямо в string.

Подвох: сбросьте перед повторным использованием

Ловушка, в которую попадают все, - это повторное использование потока без сброса. Как только поток достигает конца своих данных, его флаги eof и fail защёлкиваются, и каждый последующий >> тихо ничего не делает - никакой ошибки, лишь устаревшие значения:

stringstream ss("10");
int a, b;
ss >> a;            // a = 10, поток теперь в конце -> флаг eof установлен
ss.str("20");       // загрузить новый текст...
ss >> b;            // ТИХО ПРОВАЛИВАЕТСЯ - флаги ошибок всё ещё установлены, b не меняется

Нужно очистить состояние ошибки и содержимое. Правильный порядок - сначала clear(), затем str():

Две связанные ошибки: ss.clear() сбрасывает флаги, но не опустошает буфер (для этого используйте ss.str("")), а stringstream, к которому вы продолжаете дописывать с помощью <<, будет продолжать расти - создавайте новый внутри цикла, а не используйте повторно грязный на каждой итерации.

Далее: массивы

Строковые потоки дали вам чистый способ превращать текст в типизированные значения и обратно - часто целый их список, как vector, который вы заполнили при разбиении CSV. Чтобы хранить и индексировать фиксированные коллекции этих значений, вам нужен самый фундаментальный контейнер языка. Далее мы рассмотрим массивы: как их объявлять, индексировать, перебирать и избегать доступа за границы, который вызывает столько неопределённого поведения в C++.

Часто задаваемые вопросы

Что такое stringstream в C++?

std::stringstream - это поток, опирающийся на string, а не на клавиатуру или файл. Вы пишете в него с помощью << и читаете из него с помощью >>, точно так же как с cout и cin, что делает его стандартным инструментом для разбора текста и сборки строк в памяти. Он находится в заголовке <sstream>.

Как преобразовать строку в int в C++ с помощью stringstream?

Поместите текст в поток и извлеките его в число: stringstream ss("42"); int n; ss >> n;. Проверьте if (ss) (или if (ss >> n)), чтобы убедиться, что преобразование действительно прошло успешно. Для простых случаев std::stoi короче, но stringstream блистает, когда одна строка содержит несколько разнотипных значений.

Почему нужно вызывать clear() у stringstream?

Как только поток достигает конца своих данных, его биты eof/fail остаются установленными, и каждый последующий >> тихо ничего не делает. Если вы повторно используете тот же stringstream с новым содержимым, вызовите ss.clear(), чтобы сбросить эти флаги ошибок, а затем ss.str(newText), чтобы загрузить свежие данные - иначе чтения будут молча проваливаться.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ