Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
86
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

1035

20.2.1. Строковый ввод

Считывание можно производить как в C-строки, так и в объекты класса string. Мы рекомендуем пользоваться последними. Их главное преимущество автоматическое управление памятью для хранения символов. Чтобы прочитать данные в C-строку, т.е. массив символов, необходимо сначала задать его размер, достаточный для хранения строки. Обычно мы читаем символы в буфер, затем выделяем из хипа ровно столько памяти, сколько нужно для хранения прочитанной строки, и копируем данные из буфера

#include <iostream> #include <string.h>

char inBuf[ 1024 ]; try

{

while ( cin >> inBuf ) {

char *str = new char[ strlen( inBuf ) + 1 ]; strcpy( str, inBuf );

// ... сделать что-то с массивом символов str delete [] str;

}

}

в эту память:

catch( ... ) { delete [] str; throw; }

#include <iostream> #include <string.h>

string str;

while ( cin >> str )

Работать с типом string значительно проще:

// ... сделать что-то со строкой

Рассмотрим операторы ввода в C-строки и в объекты класса string. В качестве входного текста по-прежнему будет использоваться рассказ об Алисе Эмме:

Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful fiery bird, he tells her, magical but untamed. "Daddy, shush,

there is no such creature," she tells him, at the same time wanting him to tell her more. Shyly, she asks, "I mean, Daddy, is there?"

Поместим этот текст в файл alice_emma, а затем перенаправим на него стандартный вход программы. Позже, когда мы познакомимся с файловым вводом, мы откроем и прочтем этот файл непосредственно. Следующая программа помещает прочитанные со стандартного ввода слова в C-строку и находит самое длинное слово:

С++ для начинающих

1036

#include <iostream.h> #include <string.h>

int main()

{

const int bufSize = 24;

char buf[ bufSize ], largest[ bufSize ];

// для хранения статистики int curLen, max = -1, cnt = 0; while ( cin >> buf )

{

curLen = strlen( buf ); ++cnt;

// новое самое длинное слово? сохраним его if ( curLen > max ) {

max = curLen;

strcpy( largest, buf );

}

}

cout << "Число прочитанных слов " << cnt << endl;

cout << "Длина самого длинного слова " << max << endl;

cout << "Самое длинное слово " << largest << endl;

}

После компиляции и запуска программа выводит следующие сведения:

Число прочитанных слов 65 Длина самого длинного слова 10 Самое длинное слово creature,"

На самом деле этот результат неправилен: самое длинное слово beautiful, в нем девять букв. Однако выбрано creature, потому что программа сочла его частью запятую и кавычку. Следовательно, необходимо отфильтровать небуквенные символы.

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

while ( cin >> setw( bufSize ) >> buf )

Здесь bufSize размер массива символов buf. setw() разбивает строку длиной bufSize или больше на несколько строк, каждая из которых не длиннее, чем bufSize - 1.

Завершается такая частичная строка двоичным нулем. Для использования setw() в программу необходимо включить заголовочный файл iomanip:

#include <iomanip>

С++ для начинающих

1037

Если в объявлении массива buf размер явно не указан:

char buf[] = "Нереалистичный пример";

то программист может применить оператор sizeof, но при условии, что идентификатор является именем массива и находится в области видимости выражения:

while ( cin >> setw(sizeof( buf )) >> buf )

#include <iostream> #include <iomanip>

int main()

{

const int bufSize = 24; char buf[ bufSize ]; char *pbuf = buf;

//если строка длиннее, чем sizeof(char*),

//она разбивается на несколько строк

while ( cin >> setw( sizeof( pbuf )) >> pbuf ) cout << pbuf << endl;

Применение оператора sizeof в следующем примере дает неожиданный результат:

}

Программа печатает:

$ a.out

The winter of our discontent

The win ter of our dis con ten t

Функции setw() вместо размера массива передается размер указателя, длина которого на нашей машине равна четырем байтам, поэтому вывод разбит на строки по три символа.

Попытка исправить ошибку приводит к еще более серьезной проблеме:

while ( cin >> setw(sizeof( *pbuf )) >> pbuf )

Мы хотели передать setw() размер массива, адресуемого pbuf. Но выражение

*pbuf

дает только один символ, т.е. объект типа char. Поэтому setw() передается значение 1. На каждой итерации цикла while в массив, на который указывает pbuf, помещается

С++ для начинающих

1038

только нулевой символ. До чтения из стандартного ввода дело так и не доходит, программа зацикливается.

При использовании класса string все проблемы управления памятью исчезают, об этом

#include <iostream.h> #include <string>

int main()

{

string buf, largest;

// для хранения статистики

int curLen, // длина текущего слова max = -1, // максимальная длина слова cnt = 0; // счетчик прочитанных слов

while ( cin >> buf )

{

curLen = buf.size(); ++cnt;

// новое самое длинное слово? сохраним его if ( curLen > max )

{

max = curLen; largest = buf;

}

}

cout << "Число прочитанных слов " << cnt << endl; cout << "Длина самого длинного слова " << max << endl; cout << "Самое длинное слово " << largest << endl;

заботится сам string. Вот как выглядит наша программа в данном случае:

}

Однако запятая и кавычка по-прежнему считаются частью слова. Напишем функцию для

#include <string>

void filter_string( string &str )

{

// элементы, подлежащие фильтрации string filt_elems( "\",?." ); string::size_type pos = 0;

while (( pos = str.find_first_of( filt_elems, pos )) != string::npos )

str.erase( pos, 1 );

удаления этих символов из слова:

}

Эта функция работает правильно, но множество символов, которые мы собираемся отбрасывать, “зашитов код. Лучше дать пользователю возможность самому передать строку, содержащую такие символы. Если он согласен на множество по умолчанию, то может передать пустую строку.

С++ для начинающих

1039

 

 

#include <string>

 

 

 

 

 

 

void filter_string( string &str,

 

 

 

string filt_elems = string("\",."))

 

 

 

{

 

 

 

string::size_type pos = 0;

 

 

 

while (( pos = str.find_first_of( filt_elems, pos ))

 

 

 

!= string::npos )

 

 

 

str.erase( pos, 1 );

 

 

 

}

 

 

 

 

 

 

 

Более общая версия filter_string() принимает пару итераторов, обозначающих

 

 

 

template <class InputIterator>

 

 

 

 

 

 

void filter_string( InputIterator first, InputIterator last,

 

 

 

string filt_elems = string("\",."))

 

 

 

{

 

 

 

for ( ; first != last; first++ )

 

 

 

{

 

 

 

string::size_type pos = 0;

 

 

 

while (( pos = (*first).find_first_of( filt_elems, pos ))

 

 

 

!= string::npos )

 

 

 

(*first).erase( pos, 1 );

 

 

 

}

 

диапазон, где производится фильтрация:

 

 

 

}

 

 

 

 

 

 

 

С использованием этой функции программа будет выглядеть так:

 

С++ для начинающих

1040

 

 

#include <string>

 

 

 

 

 

 

#include <algorithm>

 

 

 

#include <iterator>

 

 

 

#include <vector>

 

 

 

#include <iostream>

 

 

 

bool length_less( string s1, string s2 )

 

 

 

{ return s1.size() < s2.size(); }

 

 

 

int main()

 

 

 

{

 

 

 

istream_iterator< string > input( cin ), eos;

 

 

 

vector< string > text;

 

 

 

// copy - это обобщенный алгоритм

 

 

 

copy( input, eos, back_inserter( text ));

 

 

 

string filt_elems( "\",.;:");

 

 

 

filter_string( text.begin(), text.end(), filt_elems );

 

 

 

int cnt = text.size();

 

 

 

// max_element - это обобщенный алгоритм

 

 

 

string *max = max_element( text.begin(), text.end(),

 

 

 

length_less );

 

 

 

int len = max->size();

 

 

 

cout << "Число прочитанных слов "

 

 

 

<< cnt << endl;

 

 

 

cout << "Длина самого длинного слова "

 

 

 

<< len << endl;

 

 

 

cout << "Самое длинное слово "

 

 

 

<< *max << endl;

 

 

 

}

 

 

 

 

 

 

 

Когда мы применили в алгоритме max_element() стандартный оператор меньше”,

 

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

 

Число прочитанных слов 65 Длина самого длинного слова 4 Самое длинное слово wind

Очевидно, что wind это не самое длинное слово. Оказывается, оператор меньшев классе string сравнивает строки не по длине, а в лексикографическом порядке. И в этом смысле wind действительно максимальный элемент. Для того чтобы найти слово максимальной длины, мы должны заменить оператор меньшепредикатом length_less(). Тогда результат будет таким:

Число прочитанных слов 65 Длина самого длинного слова 9

Самое длинное слово beautiful

Упражнение 20.2