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

ЖАНРЫ

C++. Сборник рецептов

Когсуэлл Джефф

Шрифт:

Чтобы сделать автозамену работающей, требуется две вещи. Во-первых, требуется некий словарь, который содержит неправильно написанные варианты слов и связанные с ними правильные написания, map хранит пары ключ/значение, где ключ и значение могут быть любого типа, так что он является идеальным кандидатом на эту роль. В начале примера 4.31 имеется

typedef
для пар
string
:

typedef map<string, string> StrStrMap;

За более подробным описанием map обратитесь к рецепту 4.18.

TextAutoField
хранит указатель на
map
, так как, вероятнее всего, для всех полей потребуется только один общий
словарь.

Предполагая, что клиентский код помещает в

map
что-то осмысленное,
append
просто должен периодически проверять
trap
. В примере 4.31
append
ждет появления пробела или знака пунктуации. Для проверки на пробел можно использовать
isspace
, а для поиска знаков пунктуации можно использовать ispunct. Обе эти функции для узких символов определены в
<cctype>
(см. табл. 4.3).

Если вы не знакомы с использованием итераторов и методов поиска в контейнерах STL, то код, который выполняет проверку, требует некоторых пояснений,

string tmp
содержит последний фрагмент текста, который был добавлен в
TextAutoField
. Чтобы увидеть, был ли он написан с ошибками, поищите его в словаре вот так.

StrStrMap::iterator p = pDict->find(tmp);

if (p != pDict_->end) {

Здесь важно то, что

map::find
в случае успеха поиска возвращает итератор, который указывает на пару, содержащую соответствующий ключ. Если поиск не дал результатов, то возвращается итератор, указывающий на область памяти после последнего элемента
map
, на который указывает
map::end
(именно так работают контейнеры STL, поддерживающие
find
). Если слово в
map
найдено, стираем из буфера старое слово и заменяем его правильной версией.

buf_.erase(i, buf_.length - i);

buf_ += p->second;

Добавьте символ, который инициировал весь процесс (либо пробел, либо знак пунктуации), и все.

Смотри также

Рецепты 4.17, 4.18 и табл. 4.3.

4.23. Чтение текстового файла с разделителями-запятыми

Проблема

Требуется прочитать текстовый файл, чье содержимое разделено запятыми и новыми строками (или любой другой парой разделителей). Записи разделяются одним символом, а поля записи разделяются другим символом. Например, текстовый файл с разделителями-запятыми, содержащий информацию о сотрудниках, может выглядеть вот так.

Smith, Bill, 5/1/2002, Active

Stanford, John, 4/5/1999, Inactive

Такие файлы обычно временно хранят наборы данных, экспортируемые из электронных таблиц, баз данных или других форматов файлов.

Решение

Пример 4.32 демонстрирует, как это делается. Если читать текст в

string
непрерывными кусками с помощью
getline
(шаблон функции определен в
<string>
), то для анализа текста и создания структуры данных можно использовать функцию
split
, которая была представлена в рецепте 4.6.

Пример 4.32. Чтение файла с разделителями

#include <iostream>

#include <fstream>

#include <string>

#include <vector>

using namespace std;

void split(const string& s, char c, vector<string>& v) {

 int i = 0;

 int j = s.find(c);

 while (j >= 0) {

v.push_back(s.substr(i, j-i));

i = ++j;

j = s.find(c, j);

if (j < 0) {

v.push_back(s.substr(i, s.length));

}

 }

}

void loadCSV(istream& in, vector<vector<string>*>& data) {

 vector<string>* p = NULL;

 string tmp;

 while (!in.eof) {

getline(in, tmp, '\n'); //
Получить следующую строку

p = new vector<string>;

split(tmp, '.', *p); // Использовать split из

// Рецепта 4.7

data.push_back(p);

cout << tmp << '\n';

tmp.clear;

 }

}

int main(int argc, char** argv) {

 if (argc < 2)

return(EXIT_FAILURE);

 ifstream in(argv[1]);

 if (!in)

return(EXIT_FAILURE);

 vector<vector<string>*> data;

 loadCSV(in, data);

 // Выполнить с данными какие-либо действия...

 for (vector<vector<string>*>::iterator p = data.begin;

p != data end; ++p) {

delete *p; // Убедитесь, что p

 } // разыменован!

}

Обсуждение

В примере 4.32 почти нет ничего, что еще не было бы описано,

getline
обсуждается в рецепте 4.19, a
vector
— в рецепте 4.3. Единственный фрагмент, заслуживающий упоминания, — это выделение памяти.

loadCSV
создает новый
vector
для каждой прочитанной строки данных и сохраняет его в другом vector, состоящем из указателей на
vector
. Так как память для каждого из этих векторов выделяется из кучи, кто-то должен удалить ее, и этот кто-то — это вы (а не реализация
vector
).

vector
ничего не знает о том, содержит ли он значение или указатель на значение или что-либо еще. Все, что он знает, — это то, что при его удалении он должен вызвать деструктор для каждого содержащегося в нем элемента. Если
vector
хранит объекты, то все нормально, объект будет удален правильно. Но если
vector
содержит указатели, то удалены будут указатели, а не объекты, на которые они указывают.

Есть два способа гарантировать освобождение памяти. Первый заключается в том, что сделано в примере 4.32 вручную, как здесь.

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