|
Обрабатываем знаки препинания
После того как мы разбили каждую строку на слова, необходимо избавиться от знаков препинания. Пока из строки
magical but untamed. "Daddy, shush, there is no such thing,"
у нас получился такой набор слов:
magical
but
untamed.
"Daddy,
shush,
there
is
no
such
thing,"
Как нам теперь удалить ненужные знаки препинания? Для начала определим строку, содержащую все символы, которые мы хотим удалить:
string filt_elems( "\",.;:!?)(\\/" );
(Обратная косая черта указывает на то, что следующий за ней символ должен в данном контексте восприниматься буквально, а не как специальная величина. Так, \" обозначает символ двойной кавычки, а не конец строки, а \\ – символ обратной косой черты.)
Теперь можно применить функцию-член find_first_of() для поиска всех вхождений нежелательных символов:
while (( pos = word.find_first_of( filt_elems, pos ))
| != string::npos )
Найденный символ удаляется с помощью функции-члена erase():
word.erase(pos,1);
Первый аргумент этой функции означает позицию подстроки, а второй – ее длину. Мы удаляем один символ, находящийся в позиции pos. Второй аргумент является необязательным; если его опустить, будут удалены все символы от pos до конца строки.
Вот полный текст функции filter_text(). Она имеет два параметра: указатель на вектор строк, содержащий текст, и строку с символами, которые нужно убрать.
void
filter_text( vector<string> *words, string filter )
{
vector<string>::iterator iter = words->begin();
vector<string>::iterator iter_end = words->end();
// Если filter не задан, зададим его сами
if ( ! filter.size() )
filter.insert( 0, "\".," );
while ( iter != iter_end ) {
string::size_type pos = 0;
// удалим каждый найденный элемент
while (( pos = (*iter).find_first_of( filter, pos ))
!= string::npos )
(*iter).erase(pos,1);
iter++;
}
| }
Почему мы не увеличиваем значение pos на каждой итерации? Что было бы, если бы мы написали:
while (( pos = (*iter).find_first_of( filter, pos ))
!= string::npos )
{
(*iter).erase(pos,1);
++ pos; // неправильно...
| }
Возьмем строку
thing,"
На первой итерации pos получит значение 5 , т.е. позиции, в которой находится запятая. После удаления запятой строка примет вид
thing"
Теперь в 5-й позиции стоит двойная кавычка. Если мы увеличим значение pos, то пропустим этот символ.
Так мы будем вызывать функцию filter_text():
string filt_elems( "\",.;:!?)(\\/" );
| filter_text( text_locations->first, filt_elems );
А вот часть распечатки, сделанной тестовой версией filter_text():
filter_text: untamed.
found! : pos: 7.
after: untamed
filter_text: "Daddy,
found! : pos: 0.
after: Daddy,
found! : pos: 5.
after: Daddy
filter_text: thing,"
found! : pos: 5.
after: thing"
found! : pos: 5.
after: thing
filter_text: "I
found! : pos: 0.
after: I
filter_text: Daddy,
found! : pos: 5.
after: Daddy
filter_text: there?"
found! : pos: 5.
after: there"
found! : pos: 5.
after: there
Упражнение 6.15
Напишите программу, которая удаляет все символы, кроме STL из строки:
"/.+(STL).$1/"
используя сначала erase(pos,count), а затем erase(iter,iter).
Упражнение 6.16
Напишите программу, которая с помощью разных функций вставки из строк
string sentence( "kind of" );
string s1 ( "whistle" )
| string s2 ( "pixie" )
составит предложение
"A whistling-dixie kind of walk"
Приводим слова к стандартной форме
Одной из проблем при разработке текстовых поисковых систем является необходимость распознавать слова в различных словоформах, такие, как cry, cries и cried, baby и babies, и, что гораздо проще, написанные заглавными и строчными буквами, например home и Home. Первая задача, распознавание словоформ, слишком сложна, поэтому мы приведем здесь ее заведомо неполное решение. Сначала заменим все прописные буквы строчными:
void
strip_caps( vector<string,allocator> *words )
{
vector<string,allocator>::iterator iter=words->begin() ;
vector<string,allocator>::iterator iter_end=words->end() ;
string caps( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" );
while ( iter != iter_end ) {
string::size_type pos = 0;
while (( pos = (*iter).find_first_of( caps, pos ))
!= string::npos )
(*iter)[ pos ] = to1ower( (*iter)[pos] );
++iter;
}
| }
Функция
to1ower( (*iter)[pos] );
входит в стандартную библиотеку С. Она заменяет прописную букву соответствующей ей строчной. Для использования tolower() необходимо включить заголовочный файл:
#include <ctype.h>
(В этом файле объявлены и другие функции, такие, как isalpha(), isdigit(), ispunct(), isspace(), toupper(). Полное описание этих функций см. [PLAUGER92]. Стандартная библиотека С++ включает класс ctype, который инкапсулирует всю функциональность стандартной библиотеки Си, а также набор функций, не являющихся членами, например toupper(), tolower() и т.д. Для их использования нужно включить заголовочный файл
#include <locale>
Однако наша реализация компилятора еще не поддерживала класс ctype, и нам пришлось использовать стандартную библиотеку Си.)
Проблема словоформ слишком сложна для того, чтобы пытаться решить ее в общем виде. Но даже самый примитивный вариант способен значительно улучшить работу нашей поисковой системы. Все, что мы сделаем в данном направлении, – удалим букву 's' на концах слов:
void suffix_text( vector<string,allocator> *words )
{
vector<string,allocator>::iterator
iter = words->begin(),
iter_end = words->end();
while ( iter != iter_end ) {
// оставим слова короче трех букв как есть
if ( (*iter).size() <= 3 )
{ ++iter; continue; }
if ( (*iter)[ (*iter).size()-1 ] == 's' )
suffix_s( *iter );
// здесь мы могли бы обработать суффиксы
// ed, ing, 1y
++iter;
}
| }
Слова из трех и менее букв мы пропускаем. Это позволяет оставить без изменения, например, has, its, is и т.д., однако слова tv и tvs мы не сможем распознать как одинаковые.
Если слово кончается на "ies", как babies и cries, необходимо заменить "ies" на "y":
string::size_type pos() = word.size()-3;
string ies( "ies" );
if ( ! word.compare( pos3, 3, ies )) {
word.replace( pos3, 3, 1, 'у' );
return;
| }
compare() возвращает 0, если две строки равны. Первый аргумент, pos3, обозначает начальную позицию, второй – длину сравниваемой подстроки (в нашем случае 3). Третий аргумент, ies, – строка-эталон. (На самом деле существует шесть вариантов функции compare(). Остальные мы покажем в следующем разделе.)
replace() заменяет подстроку набором символов. В данном случае мы заменяем подстроку "ies" длиной в 3 символа единичным символом 'y'. (Имеется десять перегруженных вариантов функции replace(). В следующем разделе мы коснемся остальных вариантов.)
Если слово заканчивается на "ses", как promises или purposes, нужно удалить суффикс "es"[16]:
string ses( "ses" );
if ( ! word.compare( pos3, 3, ses )) {
word.erase( pos3+l, 2 );
return;
| }
Если слово кончается на "ous", как oblivious, fulvous, cretaceous, или на "is", как genesis, mimesis, hepatitis, мы не будем изменять его. (Наша система несовершенна. Например, в слове kiwis надо убрать последнее 's'.) Пропустим и слова, оканчивающиеся на "ius" (genius) или на "ss" (hiss, lateness, less). Нам поможет вторая форма функции compare():
string::size_type spos = 0;
string::size_type pos3 = word.size()-3;
// "ous", "ss", "is", "ius"
string suffixes( "oussisius" );
if ( ! word.compare( pos3, 3, suffixes, spos, 3 ) || // ous
! word.compare( pos3, 3, suffixes, spos+6, 3 ) || // ius
! word.compare( pos3+l, 2, suffixes, spos+2, 2 ) || // ss
! word.compare( pos3+l, 2, suffixes, spos+4, 2 ) ) // is
| return;
В противном случае удалим последнее 's':
word.erase( pos3+2 );
Имена собственные, например Pythagoras, Brahms, Burne-Jones, не подпадают под общие правила. Этот случай мы оставим как упражнение для читателя, когда будем рассказывать об ассоциативных контейнерах.
Но прежде чем перейти к ним, рассмотрим оставшиеся строковые операции.
Упражнение 6.17
Наша программа не умеет обрабатывать суффиксы ed (surprised), ly (surprisingly) и ing (surprisingly). Реализуйте одну из функций для этого случая:
(a) suffix_ed() (b) suffix_ly() (c) suffix_ing()
Не нашли, что искали? Воспользуйтесь поиском по сайту:
©2015 - 2024 stydopedia.ru Все материалы защищены законодательством РФ.
|