<< Пред. стр. 27 (из 121) След. >>
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
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
locat = text_map[ query ];
Ключом здесь является строка, а значение имеет тип location
Для использования отображения необходимо включить соответствующий заголовочный файл:
#include