Вторник, 23.12.2025 15:00

Обработка строк в C++

Обработка строк в C++

При работе со строками в C++ вам, возможно, придется заниматься очисткой строк от лишних пробелов, а так же разделением их на части, особенно, если вы работаете с фамилиями или адресами, загружаемыми из внешних источников.

Сегодня мы рассмотрим создания класса C++ для работы со строками, а именно реализуем функции ltrim(), rtrim() и trim() для std::string. Так же мы рассмотрим итераторы при работе со строками std::string и напишем функцию split() для разделения строки используя заданные разделители.

Подготовка

Для этого проекта я буду использовать новый шаблон, для прострого проекта на C++ с использованием CMake.

Загрузить исходный код вы можете из репозитория - https://gitflic.ru/project/vasiliyaltunin/cpp_tempalte_app

Создание класса

В папку src добавим файл stringutils.h с содержимым:

#ifndef STRINGUTILS_H
#define STRINGUTILS_H

#pragma once

class StringUtils
{
public:
StringUtils();
~StringUtils();

private:
};

#endif

И файл stringutils.cpp с содержимым:

#include "stringutils.h"

StringUtils::StringUtils()
{
}
StringUtils::~StringUtils()
{
}

В файл cmake/src.cmake.in добавим файлы .h и .cpp нашего класса, приведя его к вид:

# Путь к файлам с исходным кодом
set(MY_SRC 
"src/helloworld.h"
"src/helloworld.cpp"
"src/stringutils.h"
"src/stringutils.cpp"
"main.cpp")

Удаляем все пробелы из конца строки

При работе со строками, особенно когда программа ожидает от пользователя ввода текста, хорошей идеей является очистка строки от пробелов в начале и конце строки которые пользователь может поставить случайно или они попадут в нее при копировании/вставке из другой программы.

Добавим метод удаляющий пробелы из конца строки.

В файл stringutils.h добавим:

#include <string>

Добавим метод rtim() в класс StringUtils:

std::string StringUtils::rtrim(std::string sourceStr);

В файл stringutils.cpp добавим реализацию метода:

std::string StringUtils::rtrim(std::string sourceStr) {
{
  int spacePos = sourceStr.find_last_not_of(" ") + 1;
  
  std::cout << "\nFirst space=" << spacePos << "\n";
  
  sourceStr.erase(sourceStr.find_last_not_of(" ") + 1);
  
  return sourceStr;
}

В файл main.cpp добавим код:

std::string str = "HELLO WORLD   ";

std::cout << "\n|" << str << "|\n";

str = s.rtrim(str);

std::cout << "\n|" << str << "|\n";

Обратите внимание на строку:

HELLO WORLD    
01234567890123

У нас три пробела с конца строки а сама строка длинной 13 символов!

Запустим конфигурирование и сборку и запустим:

cmake -S . -B build & cmake --build build & .\build\cppapp.exe

Результат:

|HELLO WORLD |
First space=11
|HELLO WORLD|

В методе rtrim() мы сначала получаем позицию первого символа, который отличается от любого из символов, переданных в параметре фикции find_last_not_of.

В нашем случае это любой символ не являющийся пробелом. Метод find_last_not_of() производит поиск в строке начиная с самого последнего символа и двигается к началу строки пока не найдет первый подходящий символ и возвращает его позицию в строке.

Далее мы добавляем к результату единицу, так как нам нужна позиция первого пробела с конца строки и удаляем все символы после этой позиции до конца строки с помощью метода erase().

Аналогично добавим метод для удаления пробелов с начала строки.

Добавим в начало строки два пробела.

std::string str = " HELLO WORLD ";

Добавим в класс StringUtils метод:

std::string StringUtils::ltrim(std::string sourceStr) {

  int spacePos = sourceStr.find_first_not_of(" ");

  std::cout << "\nFirst space=" << spacePos << "\n";

  sourceStr.erase(0, sourceStr.find_first_not_of(" "));

  return sourceStr;
}

Добавим в main.cpp

str = s.ltrim(str);
std::cout << "\n|" << str << "|\n";

Запустим сборку и программу:

| HELLO WORLD |
First space=13
| HELLO WORLD|
First space=2
|HELLO WORLD|

Мы удалили пробелы из начала и из конца строки.

Удалим избыточный код из методов, приведя к виду:

std::string StringUtils::rtrim(std::string sourceStr) {
  sourceStr.erase(sourceStr.find_last_not_of(" ") + 1);
  return sourceStr;
}
std::string StringUtils::ltrim(std::string sourceStr) {
  sourceStr.erase(0, sourceStr.find_first_not_of(" "));
  return sourceStr;
}

Универсальный метод для удаления пробелов с начала и с конца строки

В класс StringUtils добавим метод:

std::string StringUtils::trim(std::string sourceStr) {
  return StringUtils::ltrim(StringUtils::rtrim(sourceStr));
}

В файл main.cpp добавим строки:

str = " HELLO WORLD ";

std::cout << "\n|" << str << "|\n";

str = s.trim(str);

std::cout << "\n|" << str << "|\n";

Запустим:

| HELLO WORLD |
|HELLO WORLD|

Очищаем строку от невидимых символов

Мы написали метод, который очищает строку от пробелов с начала и конца. Но помимо пробелов, в строке могут попасться прочие символы, которые мы захотим удалить, например символ табуляции или возврата строки. Давайте модифицируем методы класса StringUtils для очистки строки и от таких символов.

Объявим строку, в которой перечислим все символы, которые мы хотим удалять:

Перед объявлением класса добавим:

const std::string TRIMABLES = " \t\n\r";

Здесь мы добавили символы пробела, табуляции, новой строки и возврата каретки. При необходимости вы можете добавить прочие символы для удаления.

В методы rtim и ltrim добавим нашу строку TRIMABLES изменив их следующим образом:

std::string StringUtils::rtrim(std::string sourceStr) {
  sourceStr.erase(sourceStr.find_last_not_of(TRIMABLES) + 1);
  return sourceStr;
}
std::string StringUtils::ltrim(std::string sourceStr) {
  sourceStr.erase(0, sourceStr.find_first_not_of(TRIMABLES));
  return sourceStr;
}

Запустим, всё работает как и раньше, даже если мы добавим в конец строки символ \n или \t.

Разбиваем строку на части с использованием пробелов и разделителей

Очень часто при работе со строками может потребоваться разделять их на части. Например при работе с ФИО людей может потребоваться превратить строку в массив, содержащий фамилию, имя и отчество.

Здесь мы сразу объявим новый тип - strlist, так как часто будем использовать его далее и он позволит нам существенно сократить программный код. 

Перед объявлением класса StringUtils добавим:

typedef std::list<std::string> strlist;

Добавим в класс StringUtils метод split:

std::list<std::string> StringUtils::split(std::string sourceStr,
                                                 char splitter) {
  strlist data;
  std::string tmp;
  
  for (char buffer : sourceStr) {
    if (buffer == splitter) {
      data.insert(data.end(), tmp);
      tmp.clear();
    } else {
      tmp.append(1, buffer);
    }
  }
  
  data.insert(data.end(), tmp);
  
  return data;
}

Здесь мы используем итератор для обхода всей строки, когда нам встречается символ разделителя, все предыдущие символы мы добавляем в качестве нового элемента списка в виде строки std::string. В самом конце мы добавляем все оставшиеся символы в новый элемент списка и возвращаем сам список.

Чтобы добавить строку в конец списка мы получаем итератор конца списка с помощью data.end().

Добавим в main.cpp строки:

str = "HELLO MY NEW PERFECT WORLD";

std::cout << "\n|" << str << "|\n";

strlist result;

result = s.split(str, ' ');

for (std::string s : result) {
  std::cout << s << "=";
}

Запустим:

|HELLO MY NEW PERFECT WORLD|
HELLO=MY=NEW=PERFECT=WORLD=

Мы получили список из всех слов строки.

Мы так же можем использовать другой разделитель, например код:

str = "HELLO MY NEW|PERFECT WORLD";

result = s.split(str, '|');

for (std::string s : result) {
  std::cout << s << "=";
}

Выведет:

HELLO MY NEW=PERFECT WORLD=

Заключение

Сегодня мы создали класс для работы со строками:

Создали класс StringUtils;

Добавили метод для удаления пробелов с конца строки;

Добавили метод для удаления пробелов из начала строки;

Добавили методы для удаления пробелов и с начала и с конца строки;

Добавили константу для хранения списка невидимых символов, подлежащих удалению и доработали методы очистки строки от пробелов;

Добавили метод разбивающий строку на на части с использованием пробелов или любого символа указанного в параметре.

Категория C++

Добавить комментарий

Простой текст

  • HTML-теги не обрабатываются и показываются как обычный текст
  • Строки и абзацы переносятся автоматически.
  • Адреса веб-страниц и email-адреса преобразовываются в ссылки автоматически.
Просмотров: 53