Чтение онлайн

ЖАНРЫ

Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:

Глава 23

Обработка текста

“Ничто не может быть настолько очевидным,

чтобы быть действительно очевидным...

Употребление слова “очевидно” свидетельствует

об отсутствии логических аргументов”.

Эррол Моррис (Errol Morris)

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

string
,
iostream
и
map
. Затем введем регулярные выражения (класс
regex
), позволяющие выражать шаблонные фрагменты текстов. В заключение покажем, как с помощью регулярных выражений находить и извлекать из текста специфические элементы данных, такие как почтовые индексы, а также верифицировать форматы текстовых файлов.

23.1. Текст

По существу, мы постоянно работаем с текстом. Наши книги заполнены текстом, большая часть того, что мы видим на экране компьютера, — это текст, и исходный код наших программ является текстом. Наши каналы связи (всех видов) переполнены словами. Всю информацию, которой обмениваются два человека, можно было бы представить в виде текста, но не будем заходить так далеко. Изображения и звуки обычно лучше всего представлять в виде изображений и звуков (т.е. в виде совокупности битов), но все остальное можно обрабатывать с помощью программ анализа и преобразования текста.

Начиная с главы 3 мы использовали классы

iostreams
и
string
, поэтому здесь кратко опишем библиотеки, которым они принадлежат. Особенно полезны для обработки текстов ассоциативные массивы (раздел 23.4), поэтому мы приводим пример их использования для анализа электронной почты. Кроме этого обзора, в главе рассматриваются вопросы поиска шаблонных фрагментов в тексте с помощью регулярных выражений (разделы 23.5–23.10).

23.2. Строки

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

string
) можно найти в справочнике или учебнике повышенной сложности. Эти операции определены в заголовке
<string>
(но не
<string.h>
).

Операции ввода-вывода описаны в главах 10-11, а также в разделе 23.3. Обратите внимание на то, что операции ввода в объект класса string при необходимости увеличивают его размер, поэтому переполнение никогда не происходит.

Операции

insert
и
append
перемещают символы, чтобы освободить место для новых. Операция
erase
сдвигает символы влево, чтобы заполнить пробел, оставшийся после удаления символа.

На самом деле стандартная строка в библиотеке описывается шаблонным классом
basic_string
, поддерживающим множество наборов символов, например, Unicode, в котором предусмотрены тысячи символов (таких как ?, ?, ?, ?, ?, и ?, кроме обычных символов). Скажем, если у вас есть шрифт, содержащий символ из набора Unicode, например Unicode, можете написать следующий фрагмент кода:

basic_string<Unicode> a_unicode_string;

Стандартный класс

string
, который мы используем, является просто классом
basic_string
, конкретизированным
обычным типом
char
.

typedef basic_string<char> string; // строка — это basic_string<char>

Мы не будем описывать символы или строки кода Unicode, но при необходимости вы можете работать с ними точно так же, как и с обычными символами и строками (к ним применяются точно такие же конструкции языка, класс
string
, потоки класса
iostream
и регулярные выражения). Если вам нужны символы кода Unicode, то лучше всего попросить совета у опытных пользователей; для того чтобы ваша программа стала полезной, вы должны не только выполнять правила языка, но и некоторые системные соглашения.

В контексте обработки текста важно помнить, что практически все можно представить в виде строки символов. Например, на этой странице число

12.333
представлено в виде строки, состоящей из шести символов и окруженной пробелами.

Если вы считываете это число, то должны сначала превратить эти символы в число с плавающей точкой и лишь потом применять к нему арифметические операции. Это приводит к необходимости конвертирования чисел в объекты класса
string
и объектов класса
string
в числа. В разделе 11.4 мы видели, как превратить целое число в объект класса
string
, используя класс ostringstream. Этот прием можно обобщить для любого типа, имеющего оператор
<<
.

template<class T> string to_string(const T& t)

{

ostringstream os;

os << t;

return os.str;

}

Рассмотрим пример.

string s1 = to_string(12.333);

string s2 = to_string(1+5*6–99/7);

Значение строки

s1
равно "
12.333
", а значение строки
s2
— "
17
". Фактически функцию
to_string
можно применять не только к числовым значениям, но и к любому классу
T
с оператором
<<
.

Обратное преобразование, из класса

string
в число, так же просто, как и полезно.

struct bad_from_string:std::bad_cast

// класс для сообщений об ошибках при преобразовании строк

{

const char* what const // override bad_cast’s what

{

return "bad cast from string";

}

};

template<class T> T from_string(const string& s)

{

istringstream is(s);

T t;

if (!(is >> t)) throw bad_from_string;

return t;

}

Рассмотрим пример.

double d = from_string<double>("12.333");

void do_something(const string& s)

try

{

int i = from_string<int>(s);

Поделиться с друзьями: