Имеется класс, который представляет некий тип текстового поля или документа, и по мере добавления в него текста требуется автоматически корректировать неправильно написанные слова, как это делает функция Autocorrect (Автозамена) в Microsoft Word.
Решение
Это можно реализовать в относительно небольшом коде, если использовать
map
, который определен в
<map>
,
string
и различные возможности стандартной библиотеки.
Пример 4.31 показывает, как это делается.
Пример 4.31. Автозамена текста
#include <iostream>
#include <string>
#include <cctype>
#include <map>
using namespace std;
typedef map<string, string> StrStrMap;
// Класс для хранения текстовых полей
class TextAutoField {
public:
TextAutoField(StrStrMap* const p) : pDict_(p) {}
~TextAutoField {}
void append(char c);
void getText(string& s) {s = buf_;}
private:
TextAutoField;
string buf_;
StrStrMap* const pDict ;
};
// Добавление с автозаменой
void TextAutoField::append(char c) {
if ((isspace(c) || ispunct(c)) && // Выполнять автоза-
buf_.length > 0 && // мену, только когда вводятся
!isspace(buf_[buf_.length - 1])) { // ws или punct
string::size_type i = buf_.find_last_of(" \f\n\r\t\v");
i = (i == string::npos) ? 0 : ++i;
string tmp = buf_.substr(i, buf_.length - i);
StrStrMap::const_iterator p = DDict_->find(tmp);
if (p != pDict_->end) { // Нашли, так что стираем
buf_.erase(i, buf_.length - i); // и заменяем
buf_ += p->second;
}
}
buf_ += с;
}
int main {
// Создаем map
StrStrMap dict;
TextAutoField txt(&dict);
dict["taht"] = "that";
dict["right"] = "wrong";
dict["bug"] = "feature";
string tmp = "He's right, taht's a bug.";
cout << "Оригинальная версия: " << tmp << '\n';
for (string::iterator p = tmp.begin; p != tmp.end; ++p) {
txt.append(*p);
}
txt.getText(tmp);
cout << "Исправленная
версия. " << tmp << '\n';
}
Вывод примера 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
найдено, стираем из буфера старое слово и заменяем его правильной версией.