Язык программирования C++. Вводный курс

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


Считывание можно производить как в 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; }

Работать с типом string

значительно проще:

#include <iostream>

#include <string.h>

string str;

while ( cin >> str )

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

Рассмотрим операторы ввода в 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-строку и находит самое длинное слово:

#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>

Если в объявлении массива buf

размер явно не указан:

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

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

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

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



#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;
<


}

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

$ 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, помещается только нулевой символ. До чтения из стандартного ввода дело так и не доходит, программа зацикливается.

При использовании класса string все проблемы управления памятью исчезают, об этом заботится сам 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;
}

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



#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 );
<


}

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



#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 );

           }
}

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



#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

Прочитайте из стандартного ввода последовательность данных таких типов: string, double, string, int, string. Каждый раз проверяйте, не было ли ошибки чтения.

Упражнение 20.3

Прочитайте из стандартного ввода заранее неизвестное число строк. Поместите их в список. Найдите самую длинную и самую короткую строку.


Содержание раздела