txt.append(*p);
}
txt.getText(tmp);
cout << 'Исправленная версия. ' << tmp << '
';
}
Вывод примера 3.2 таков.
Оригинальная версия: He's right, taht's a bug.
Исправленная версия: He's wrong, that's a feature.
string
и map
удобны в ситуациях, когда требуется отслеживать ассоциации string
. TextAutoField
— это простой текстовый буфер, использующий string
для хранения данных. Интересной TextAutoField
делает ее метод append
, который «слушает» пробелы или знаки пунктуации и при их появлении выполняет обработку.
Чтобы сделать автозамену работающей, требуется две вещи. Во-первых, требуется некий словарь, который содержит неправильно написанные варианты слов и связанные с ними правильные написания, 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.
#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, '
'); // Получить следующую строку
p = new vector<string>();
split(tmp, '.', *p); // Использовать split из
// Рецепта 4.7
data.push_back(p);
cout << tmp << '
';
tmp.clear();
}
}