Программирование на C / C++ / Ален И. Голуб. Правила программирования на Си и Си++ [pdf]
.pdfС++ для начинающих |
282 |
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 |
Можно выделить для вставки подстроку из new_string:
)
string_object получает значение "Mississippi Belle". Если мы хотим вставить все символы new_string, начиная с pos, последний параметр нужно опустить.
string sl( "Mississippi" );
Пусть есть две строки:
string s2( "Annabelle" );
Как получить третью строку со значением "Miss Anna"?
string s3;
// скопируем первые 4 символа s1
Можно использовать функции-члены assign() и append(): s3.assign ( s1, 4 );
// добавим пробел
s3 теперь содержит значение "Miss".
s3 += ' ';
// добавим 4 первых символа s2
Теперь s3 содержит "Miss ". s3.append(s2,4);
s3 получила значение "Miss Anna". То же самое можно сделать короче:
s3.assign(s1,4).append(' ').append(s2,4);
С++ для начинающих |
283 |
Другая форма функции-члена assign() имеет три параметра: второй обозначает позицию начала, а третий – длину. Позиции нумеруются с 0. Вот как, скажем, извлечь
string beauty;
// присвоим beauty значение "belle"
"belle" из "Annabelle": 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():
С++ для начинающих |
284 |
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(), которая делает то же, что и операция взятия индекса, но с проверкой. Если индекс выходит за границу, возбуждается исключение
void
mumble( const string &st, int index )
{
try {
char ch = st.at( index ); // ...
}
catch ( std::out_of_range ){...} // ...
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 );
С++ для начинающих |
285 |
возвращает одно из трех значений:
∙если 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 new_str( "Abstract Data Type" );
несколькими способами. Допустим, как объект string: sentence.replace( position, length, new_str );
Следующий пример иллюстрирует выделение подстроки в new_str:
С++ для начинающих |
286 |
|||
|
#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]. |
|
|
С++ для начинающих |
287 |
Упражнение 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:
С++ для начинающих |
288 |
6.12. Строим отображение позиций слов
Вэтом разделе мы построим отображение (map), позволяющее для каждого уникального слова текста сохранить номера строк и колонок, в которых оно встречается. (В следующем разделе мы изучим ассоциативный контейнер set.) В общем случае контейнер set полезен, если мы хотим знать, содержится ли определенный элемент в некотором множестве, а map позволяет связать с каждым из них какую-либо величину.
Вmap хранятся пары ключ/значение. Ключ играет роль индекса для доступа к ассоциированному с ним значению. В нашей программе каждое уникальное слово текста будет служить ключом, а значением станет вектор, содержащий пары (номер строки,
string query( "pickle" ); vector< location > *locat;
// возвращается location<vector>*, ассоциированный с "pickle"
номер колонки). Для доступа применяется оператор взятия индекса. Например: locat = text_map[ query ];
Ключом здесь является строка, а значение имеет тип location<vector>*.
Для использования отображения необходимо включить соответствующий заголовочный файл:
#include <map>
Какие основные действия производятся над ассоциативными контейнерами? Их заполняют элементами или проверяют на наличие определенного элемента. В следующем подразделе мы покажем, как определить пару ключ/значение и как поместить такие пары в контейнер. Далее мы расскажем, как сформулировать запрос на поиск элемента и извлечь значение, если элемент существует.
6.12.1. Определение объекта map и заполнение его элементами
Чтобы определить объект класса map, мы должны указать, как минимум, типы ключа и значения. Например:
map<string,int> word_count;
Здесь задается объект word_count типа map, для которого ключом служит объект типа
class employee;
string, а ассоциированным с ним значением – объект типа int. Аналогично map<int,employee*> personnel;
определяет personnel как отображение ключа типа int (уникальный номер служащего) на указатель, адресующий объект класса employee.
С++ для начинающих |
289 |
typedef pair<short,short> location; typedef vector<location> loc;
Для нашей поисковой системы полезно такое отображение: map<string,loc*> text_map;
Поскольку имевшийся в нашем распоряжении компилятор не поддерживал аргументы по умолчанию для параметров шаблона, нам пришлось написать более развернутое
map<string,loc*, |
// ключ, значение |
less<string>, |
// оператор сравнения |
allocator> |
// распределитель памяти по умолчанию |
определение: text_map;
По умолчанию сортировка ассоциативных контейнеров производится с помощью операции “меньше”. Однако можно указать и другой оператор сравнения (см. раздел 12.3 об объектах-функциях).
После того как отображение определено, необходимо заполнить его парами
#include <map> #include <string>
map<string,int> word_count;
word_count[ string("Anna") ] = 1; word_count[ string("Danny") ] = 1; word_count[ string("Beth") ] = 1;
ключ/значение. Интуитивно хочется написать примерно так:
// и так далее ...
Когда мы пишем:
word_count[ string("Anna") ] = 1;
на самом деле происходит следующее:
1.Безымянный временный объект типа string инициализируется значением "Anna" и передается оператору взятия индекса, определенному в классе map.
2.Производится поиск элемента с ключом "Anna" в массиве word_count. Такого элемента нет.
3.В word_count вставляется новая пара ключ/значение. Ключом является, естественно, строка "Anna". Значением – 0, а не 1.
4.После этого значению присваивается величина 1.
С++ для начинающих |
290 |
Если элемент отображения вставляется в отображение с помощью операции взятия индекса, то значением этого элемента становится значение по умолчанию для его типа данных. Для встроенных арифметических типов – 0.
Следовательно, если инициализация отображения производится оператором взятия индекса, то каждый элемент сначала получает значение по умолчанию, а затем ему явно присваивается нужное значение. Если элементы являются объектами класса, у которого
инициализация по умолчанию и присваивание значения требуют больших затрат времени, программа будет работать правильно, но недостаточно эффективно.
// предпочтительный метод вставки одного элемента word_count.insert(
map<string,i nt>::
value_type( string("Anna"), 1 )
Для вставки одного элемента предпочтительнее использовать следующий метод:
);
В контейнере map определен тип value_type для представления хранимых в нем пар
map< string,int >::
ключ/значение. Строки
value_type( string("Anna"), 1 )
создают объект pair, который затем непосредственно вставляется в map. Для удобства чтения можно использовать typedef:
typedef map<string,int>::value_type valType;
Теперь операция вставки выглядит проще:
word_count.insert( valType( string("Anna"), 1 ));
Чтобы вставить элементы из некоторого диапазона, можно использовать метод insert(),
map< string, int > word_count; // ... заполнить
map< string,int > word_count_two;
// скопируем все пары ключ/значение
принимающий в качестве параметров два итератора. Например: word_count_two.insert(word_count.begin(),word_count.end());
Мы могли бы сделать то же самое, просто проинициализировав одно отображение другим:
С++ для начинающих |
291 |
// инициализируем копией всех пар ключ/значение
map< string, int > word_count_two( word_count );
Посмотрим, как можно построить отображение для хранения нашего текста. Функция separate_words(), описанная в разделе 6.8, создает два объекта: вектор строк, хранящий все слова текста, и вектор позиций, хранящий пары (номер строки, номер колонки) для каждого слова. Таким образом, первый объект дает нам множество значений ключей нашего отображения, а второй – множество ассоциированных с ними значений.
separate_words() возвращает эти два вектора как объект типа pair, содержащий указатели на них. Сделаем эту пару аргументом функции build_word_map(), в
// typedef для удобства чтения
typedef pair< short,short > |
location; |
typedef vector< location > |
loc; |
typedef vector< string > |
text; |
typedef pair< text*,loc* > |
text_loc; |
extern map< string, loc* >* |
|
результате которой будет получено соответствие между словами и позициями: build_word_map( const text_loc *text_locations );
Сначала выделим память для пустого объекта map и получим из аргумента-пары
map<string,loc*> *word_map = |
new map< string, loc* >; |
vector<string> *text_words |
= text_locations->first; |
указатели на векторы:
vector<location> *text_locs = text_locations->second;
Теперь нам надо синхронно обойти оба вектора, учитывая два случая:
∙слово встретилось впервые. Нужно поместить в map новую пару ключ/значение;
∙слово встречается повторно. Нам нужно обновить вектор позиций, добавив дополнительную пару (номер строки, номер колонки).
Вот текст функции: