НА ГЛАВНУЮ
Меню сайта
Категория
Ghost++ [1]
С++ [55]
Развлечение
ON - LINE
Опрос
Оцените мой сайт
Всего ответов: 199
Оbserver Ward

Онлайн всего: 1
Гостей: 1
Пользователей: 0


Друзья сайта
Заведи себе Бота
Hаша кнопка
Для обмена банерами , наша кнопка для размещения у вас на сайте

Клансайт USSR


Главная » Статьи » Программирование » С++

6. Абстрактные контейнерные типы (2)
6.8. Выделяем слова в строке

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

Alice Emma has long flowing red hair.

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

#include <string>
#include <iostream>

int main() {
   string name( "AnnaBelle" );
   int pos = name.find( "Anna" );
   if ( pos == string::npos )
      cout << "Anna не найдено!\n";
   else cout << "Anna найдено в позиции: " << pos << endl;
}

Хотя позиция подстроки почти всегда имеет тип int, более правильное и переносимое объявление типа результата, возвращаемого find(), таково:

string::size_type

Например:

string::size_type pos = name.find( "Anna" );

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

#include <string>
#include <iostream>

int main() {
   string numerics( "0123456789" );
   string name( "r2d2" );
   string:: size_type pos = name.find_first_of( numerics );
   cout << "найдена цифра в позиции: "
      << pos << "\tэлемент равен "
      << name[pos] << endl;
}

В этом примере pos получает значение 1 (напоминаем, что символы строки нумеруются с 0).
Но нам нужно найти все вхождения символа, а не только первое. Такая возможность реализуется передачей функции find_first_of() второго параметра, указывающего позицию, с которой начать поиск. Изменим предыдущий пример. Можете ли вы сказать, что в нем все еще не вполне удовлетворительно?

#include <string>
#include <iostream>
int main() {
   string numerics( "0123456789" );
   string name( "r2d2" );
   string::size_type pos = 0;

   // где-то здесь ошибка!
   while (( pos = name.find_first_of( numerics, pos ))
         != string::npos )
      cout << "найдена цифра в позиции: "
         << pos << "\tэлемент равен "
         << name[pos] << endl;
}

В начале цикла pos равно 0, поэтому поиск идет с начала строки. Первое вхождение обнаружено в позиции 1. Поскольку найденное значение не совпадает с string::npos, выполнение цикла продолжается. Для второго вызова find_first_of()значение pos равно 1. Поиск начнется с 1-й позиции. Вот ошибка! Функция find_first_of() снова найдет цифру в первой позиции, и снова, и снова... Получился бесконечный цикл. Нам необходимо увеличивать pos на 1 в конце каждой итерации:

// исправленная версия цикла
while (( pos = name.find_first_of( numerics, pos ))
         != string::npos )
{
   cout << "найдена цифра в позиции: "
        << pos << "\tэлемент равен "
        << name[pos] << endl;
   // сдвинуться на 1 символ
   ++pos;
}

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

// фрагмент программы
while (( pos = textline.find_first_of( ' ', pos ))
      != string::npos )
// ...

Чтобы узнать длину слова, введем еще одну переменную:

// фрагмент программы
// pos: позиция на 1 большая конца слова
// prev_pos: позиция начала слова
string::size_type pos = 0, prev_pos = 0;
while (( pos = textline.find_first_of( ' ', pos ))
      != string::npos )
{
  // ...

  // запомнить позицию начала слова

  prev_pos = ++pos;
}

На каждой итерации prev_pos указывает позицию начала слова, а pos – позицию следующего символа после его конца. Соответственно, длина слова равна:
pos - prev_pos; // длина слова
После того как мы выделили слово, необходимо поместить его в строковый вектор. Это можно сделать, копируя в цикле символы из textline с позиции prev_pos до pos -1. Функция substr() сделает это за нас:

// фрагмент программы
vector<string> words;
while (( pos = textline.find_first_of( ' ', pos ))
      != string::npos )
{
   words.push_back( textline.substr(
      prev_pos, pos-prev_pos));
   prev_pos = ++pos;
}

Функция substr() возвращает копию подстроки. Первый ее аргумент обозначает первую позицию, второй – длину подстроки. (Второй аргумент можно опустить, тогда подстрока включит в себя остаток исходной строки, начиная с указанной позиции.)
В нашей реализации допущена ошибка: последнее слово не будет помещено в контейнер. Почему? Возьмем строку:

seaspawn and seawrack

После каждого из первых двух слов поставлен пробел. Два вызова функции find_first_of() вернут позиции этих пробелов. Третий же вызов вернет string::npos, и цикл закончится. Таким образом, последнее слово останется необработанным.
Вот полный текст функции, названной нами separate_words(). Помимо сохранения слов в векторе строк, она вычисляет координаты каждого слова – номер строки и колонки (нам эта информация потребуется впоследствии).

typedef pair<short,short> location;
typedef vector<location> loc;
typedef vector<string> text;
typedef pair<text* ,loc*> text_loc;
text_loc*
separate_words( const vector<string> *text_file )
{
   // words: содержит набор слов
   // locations: содержит информацию о строке и позиции
   // каждого слова
   vector<string> *words = new vector<string>;
   vector<location> * locations = new vector<location>;

   short line_pos = 0; // текущий номер строки
   // iterate through each line of text
   for ( ; line_pos < text_file->size(); ++line_pos )
      // textline: обрабатываемая строка
      // word_pos: позиция в строке
      short word_pos = 0;
      string textline = (*text_file) [ line_pos ];

      string::size_type pos = 0, prev_pos = 0;

      while (( pos = textline.find_first_of( ' ', pos ))
            != string::npos )
      {
         // сохраним слово
         words->push_back(
         textline.substr( prev_pos, pos - prev_pos ));

         // сохраним информацию о его строке и позиции
         locations->push_back(
            make_pair( line_pos, word_pos ));

         // сместим позицию для следующей итерации
         ++word_pos; prev_pos = ++pos;
      }

      // обработаем последнее слово
      words->push_back(
         textline.substr( prev_pos, pos - prev_pos ));

      locations->push_back(
         make_pair( line_pos, word_pos ));
   }
  return new text_loc( words, locations );
}

  Теперь функция main()выглядит следующим образом:

int main()
{
   vector<string> *text_file = retrieve_text();
   text_loc *text_locations = separate_words( text_file );
   // ...
}

Вот часть распечатки, выданной тестовой версией separate_words():

textline: Alice Emma has long flowing red hair. Her Daddy
         says
eol: 52 pos: 5 line: 0 word: 0 substring: Alice
eol: 52 pos: 10 line: 0 word: 1 substring: Emma
eol: 52 pos: 14 line: 0 word: 2 substring: has
eol: 52 pos: 19 line: 0 word: 3 substring: long
eol: 52 pos: 27 line: 0 word: 4 substring: flowing
eol: 52 pos: 31 line: 0 word: 5 substring: red
eol: 52 pos: 37 line: 0 word: 6 substring: hair.
eol: 52 pos: 41 line: 0 word: 7 substring: Her
eol: 52 pos: 47 line: 0 word: 8 substring: Daddy
last word on line substring: says

...

textline: magical but untamed. "Daddy, shush, there is no
         such thing,"
eol: 60 pos: 7 line: 3 word: 0 substring: magical
eol: 60 pos: 11 line: 3 word: 1 substring: but
eol: 60 pos: 20 line: 3 word: 2 substring: untamed
eol: 60 pos: 28 line: 3 word: 3 substring: "Daddy,
eol: 60 pos: 35 line: 3 word: 4 substring: shush,
eol: 60 pos: 41 line: 3 word: 5 substring: there
eol: 60 pos: 44 line: 3 word: 6 substring: is
eol: 60 pos: 47 line: 3 word: 7 substring: no
eol: 60 pos: 52 line: 3 word: 8 substring: such
last word on line substring: thing,":

...

textline: Shy1y, she asks, "I mean, Daddy: is there?"
eol: 43 pos: 6 line: 5 word: 0 substring: Shyly,
eol: 43 pos: 10 line: 5 word: 1 substring: she
eol: 43 pos: 16 line: 5 word: 2 substring: asks,
eol: 43 pos: 19 line: 5 word: 3 substring: "I
eol: 43 pos: 25 line: 5 word: 4 substring: mean,
eol: 43 pos: 32 line: 5 word: 5 substring: Daddy,
eol: 43 pos: 35 line: 5 word: 6 substring: is
last word on line substring: there?":

Прежде чем продолжить реализацию поисковой системы, вкратце рассмотрим оставшиеся функции-члены класса string, предназначенные для поиска. Функция rfind() ищет последнее, т.е. самое правое, вхождение указанной подстроки:

string river( "Mississippi" );
string::size_type first_pos = river.find( "is" );
string::size_type 1ast_pos = river.rfind( "is" );

find() вернет 1, указывая позицию первого вхождения подстроки "is", а rfind() – 4 (позиция последнего вхождения "is").
find_first_not_of() ищет первый символ, не содержащийся в строке, переданной как параметр. Например, чтобы найти первый символ, не являющийся цифрой, можно написать:

string elems( "0123456789" );
string dept_code( "03714p3" );

// возвращается позиция символа 'p'
string::size_type pos = dept_code.find_first_not_of(elems) ;

find_last_of() ищет последнее вхождение одного из указанных символов. find_last_not_of() – последний символ, не совпадающий ни с одним из заданных. Все эти функции имеют второй необязательный параметр – позицию в исходной строке, с которой начинается поиск.
Упражнение 6.13

Напишите программу, которая ищет в строке

"ab2c3d7R4E6"

цифры, а затем буквы, используя сначала find_first_of(), а потом find_first_not_of().
Упражнение 6.14

Напишите программу, которая подсчитывает все слова и определяет самое длинное и самое короткое из них в строке sentence:

string linel = "We were her pride of 10 she named us --";
string line2 = "Benjamin, Phoenix, the Prodigal"
string line3 = "and perspicacious pacific Suzanne";

string sentence = linel + line2 + line3;

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

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

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"

6.10. Приводим слова к стандартной форме

Одной из проблем при разработке текстовых поисковых систем является необходимость распознавать слова в различных словоформах, такие, как 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" :

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':

// удалим последнее '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()

6.11. Дополнительные операции со строками

Вторая форма функции-члена erase() принимает в качестве параметров два итератора, ограничивающих удаляемую подстроку. Например, превратим

string name( "AnnaLiviaPlurabelle" );

в строку "Annabelle":

typedef string::size_type size_type;
size_type startPos = name.find( 'L' )
size_type endPos = name.find_1ast_of( 'b' );

name.erase( name.begin()+startPos,
name.begin()+endPos );

Символ, на который указывает второй итератор, не входит в удаляемую подстроку.
Для третьей формы параметром является только один итератор; эта форма удаляет все символы, начиная с указанной позиции до конца строки. Например:

name.erase( name. begin()+4 );

оставляет строку "Anna".
Функция-член insert() позволяет вставить в заданную позицию строки другую строку или символ. Общая форма выглядит так:

string_object.insert( position, new_string );

position обозначает позицию, перед которой производится вставка. new_string может быть объектом класса string, C-строкой или символом:

string string_object( "Missisippi" );
string::size_type pos = string_object.find( "isi" );
string_object.insert( pos+1, 's' );

Можно выделить для вставки подстроку из new_string:

string new_string ( "AnnaBelle Lee" );
string_object += ' '; // добавим пробел

// найдем начальную и конечную позицию в new_string
pos = new_string.find( 'B' );
string::size_type posEnd = new_string.find( ' ' );

string_object.insert(
   string_object.size(), // позиция вставки
   new_string, pos, // начало подстроки в new_string
   posEnd // конец подстроки new_string
)


string_object получает значение "Mississippi Belle". Если мы хотим вставить все символы new_string, начиная с pos, последний параметр нужно опустить.
Пусть есть две строки:

string sl( "Mississippi" );
string s2( "Annabelle" );

Как получить третью строку со значением "Miss Anna"?
Можно использовать функции-члены assign() и append():

string s3;
// скопируем первые 4 символа s1
s3.assign ( s1, 4 );
s3 теперь содержит значение "Miss".
// добавим пробел
s3 += ' ';

Теперь s3 содержит "Miss ".

// добавим 4 первых символа s2
s3.append(s2,4);
s3 получила значение "Miss Anna". То же самое можно сделать короче:
s3.assign(s1,4).append(' ').append(s2,4);

Другая форма функции-члена assign() имеет три параметра: второй обозначает позицию начала, а третий – длину. Позиции нумеруются с 0. Вот как, скажем, извлечь "belle" из "Annabelle":

string beauty;
// присвоим beauty значение "belle"
beauty.assign( s2, 4, 5 );

Вместо этих параметров мы можем использовать пару итераторов:

// присвоим beauty значение "belle"
beauty.assign( s2, s2.begin()+4, s2.end() );

В следующем примере две строки содержат названия текущего проекта и проекта, находящегося в отложенном состоянии. Они должны периодически обмениваться значениями, поскольку работа идет то над одним, то над другим. Например:

string current_project( "C++ Primer, 3rd Edition" );
string pending_project( "Fantasia 2000, Firebird segment" );

Функция-член swap() позволяет обменять значения двух строк с помощью вызова

current_project.swap( pending_project );

Для строки

string first_novel( "V" );

операция взятия индекса

char ch = first_novel[ 1 ];

возвратит неопределенное значение: длина строки first_novel равна 1, и единственное правильное значение индекса – 0. Такая операция взятия индекса не обеспечивает проверку правильности параметра, но мы всегда можем сделать это сами с помощью функции-члена size():

int
elem_count( const string &word, char elem )
{
   int occurs = 0;
   // не надо больше проверять ix
   for ( int ix=0; ix < word.size(); ++-ix )
      if ( word[ ix ] == elem )
         ++occurs;
   return occurs;
}

Там, где это невозможно или нежелательно, например:

void
mumble( const string &st, int index )
{
   // возможна ошибка
   char ch = st[ index ];

   // ...
}

следует воспользоваться функцией at(), которая делает то же, что и операция взятия индекса, но с проверкой. Если индекс выходит за границу, возбуждается исключение out_of_range:

void
mumble( const string &st, int index )
{
   try {
      char ch = st.at( index );
      // ...
   }
   catch ( std::out_of_range ){...}
   // ...
}

Строки можно сравнивать лексикографически. Например:

string cobol_program_crash( "abend" );
string cplus_program_crash( "abort" );

Строка cobol_program_crash лексикографически меньше, чем cplus_program_crash: сопоставление производится по первому отличающемуся символу, а буква e в латинском алфавите идет раньше, чем o. Операция сравнения выполняется функцией-членом compare(). Вызов

sl.compare( s2 );

возвращает одно из трех значений:

    * если s1 больше, чем s2, то положительное;
    * если s1 меньше, чем s2, то отрицательное;
    * если s1 равно s2, то 0.

Например,

cobol_program_crash.compare( cplus_program_crash );

вернет отрицательное значение, а

cplus_program_crash.compare( cobol_program_crash );

положительное. Перегруженные операции сравнения (<, >, !=, ==, <=, >=) являются более компактной записью функции compare().
Шесть вариантов функции-члена compare() позволяют выделить сравниваемые подстроки в одном или обоих операндах. (Примеры вызовов приводились в предыдущем разделе.)
Функция-член replace() дает десять способов заменить одну подстроку на другую (их длины не обязаны совпадать). В двух основных формах replace() первые два аргумента задают заменяемую подстроку: в первом варианте в виде начальной позиции и длины, во втором – в виде пары итераторов на ее начало и конец. Вот пример первого варианта:

string sentence(
      "An ADT provides both interface and implementation." );

string::size_type position = sentence.find_1ast_of( 'A' );
string::size_type length = 3;

// заменяем ADT на Abstract Data Type
sentence.repiace( position, length, "Abstract Data Type" );

position представляет собой начальную позицию, а length – длину заменяемой подстроки. Третий аргумент является подставляемой строкой. Его можно задать несколькими способами. Допустим, как объект string:

string new_str( "Abstract Data Type" );
sentence.replace( position, length, new_str );

Следующий пример иллюстрирует выделение подстроки в new_str:

#include <string>
typedef string::size_type size_type;

// найдем позицию трех букв
size_type posA = new_str.find( 'A' );
size_type posD = new_str.find( 'D' );
size_type posT = new_str.find( 'T' );

// нашли: заменим T на "Type"
sentence.replace( position+2, 1, new_str, posT, 4 );

// нашли: заменим D на "Data "
sentence.replace( position+1, 1, new_str, posD, 5 );

// нашли: заменим A на "Abstract "
sentence.replace( position, 1, new_str, posA, 9 );

Еще один вариант позволяет заменить подстроку на один символ, повторенный заданное количество раз:

string hmm( "Some celebrate Java as the successor to C++." );
string:: size_type position = hmm.find( 'J' );
// заменим Java на xxxx
hmm.repiace( position, 4, 'x', 4 );

В данном примере используется указатель на символьный массив и длина вставляемой подстроки:

const char *lang = "EiffelAda95JavaModula3";
int index[] = { 0, 6, 11, 15, 22 };

string ahhem(
      "C++ is the language for today's power programmers." );
ahhem.replace(0, 3, lang+index[1], index[2]-index[1]);

А здесь мы используем пару итераторов:

string sentence(
   "An ADT provides both interface and implementation." );

// указывает на 'A' в ADT
string: iterator start = sentence. begin()+3;

// заменяем ADT на Abstract Data Type
sentence.repiace( start, start+3, "Abstract Data Type" );

Оставшиеся четыре варианта допускают задание заменяющей строки как объекта типа string, символа, повторяющегося N раз, пары итераторов и C-строки.
Вот и все, что мы хотели сказать об операциях со строками. Для более полной информации обращайтесь к определению стандарта С++ [ISO-C++97].
Упражнение 6.18

Напишите программу, которая с помощью функций-членов assign() и append() из строк

string quote1( "When lilacs last in the dooryard bloom'd" );
string quote2( "The child "is father of the man" );

составит предложение
"The child is in the dooryard"

Упражнение 6.19

Напишите функцию:

string generate_salutation( string generic1,
   string lastname,
   string generic2,
   string::size_type pos,
   int length );

которая в строке

string generic1( "Dear Ms Daisy:" );

заменяет Daisy и Ms (миссис). Вместо Daisy подставляется параметр lastname, а вместо Ms подстрока

string generic2( "MrsMsMissPeople" );

длины length, начинающаяся с pos.
Например, вызов

string lastName( "AnnaP" );
string greetings =
      generate_salutation( generici, lastName, generic2, 5, 4 );

вернет строку:

Dear Miss AnnaP:

6.12. Строим отображение позиций слов

В этом разделе мы построим отображение (map), позволяющее для каждого уникального слова текста сохранить номера строк и колонок, в которых оно встречается. (В следующем разделе мы изучим ассоциативный контейнер set.) В общем случае контейнер set полезен, если мы хотим знать, содержится ли определенный элемент в некотором множестве, а map позволяет связать с каждым из них какую-либо величину.
В map хранятся пары ключ/значение. Ключ играет роль индекса для доступа к ассоциированному с ним значению. В нашей программе каждое уникальное слово текста будет служить ключом, а значением станет вектор, содержащий пары (номер строки, номер колонки). Для доступа применяется оператор взятия индекса. Например:

string query( "pickle" );
vector< location > *locat;

// возвращается location<vector>*, ассоциированный с "pickle"
locat = text_map[ query ];

Ключом здесь является строка, а значение имеет тип location<vector>*.
Для использования отображения необходимо включить соответствующий заголовочный файл:

#include <map>

Какие основные действия производятся над ассоциативными контейнерами? Их заполняют элементами или проверяют на наличие определенного элемента. В следующем подразделе мы покажем, как определить пару ключ/значение и как поместить такие пары в контейнер. Далее мы расскажем, как сформулировать запрос на поиск элемента и извлечь значение, если элемент существует.
Категория: С++ | Добавил: r2d2 (29.09.2011)
Просмотров: 958 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Born in Ussr
Залогиниться
Турниры

/j clan ussr /j clan cccp