Лекция № 5

Тема: «Обработка символьных строк на С++»

 

План

1. Описание строк символов

2. Инициализация строк

3. Функции С++ для обработки строк

4. Посимвольная обработка строк

 

1. Описание строк символов

В языке С++, в отличие от языка Turbo Pascal, нет специального типа для описания символьных строк (например, string).

Язык С++ имеет специальный тип, позволяющий хранить в переменной любой символ. Такой тип называется char. Например, если переменная имеет описание:

 

char ch;

 

То в программе допускается оператор присваивания вида:

 

ch=’A’;

 

В переменной сh будет храниться символ заглавной буквы а.

Однако реальные строки представляют собой набор символов. В С++ строка является обыкновенным массивом символов и описывается так же, как и обычные числовые массивы, то есть допускается описание вида:

 

char str[20];

 

При работе с такой строкой необходимо учитывать следующее:

 

2. Инициализация строк

Для работы со строками они должны быть проинициализированы, то есть должно быть задано некоторое значение.

Инициализацию строк можно выполнить несколькими способами.

Инициализация при описании

Строковую переменную можно проинициализировать при описании. Например:

 

char str[20]=”Программирование”;

char str1[]=”Доброе утро!”;

 

В первом случае описана строка str из 20 символов. 16 символов заполняется текстом, 17 символ становится равным null и три остаются неопределенными (заполнены мусором).

Во втором случае размер строки str1 выделяется динамически. Первые 12 символов заполняются текстом и 13 символ равен null. Длина строки точно подгоняется под фактический размер текста и равна 13 символам.

Замечание: обратите внимание, что если одиночной переменной типа char присваивается один символ, то он заключается в одинарные кавычки. Если массиву символов присваивается строка, то она заключается в двойные кавычки.

 

Инициализация через присваивание

Так как строка – это массив, то к ней недопустимо применение обычных операций присваивания, сравнения, ввода. Например, недопустима команда вида:

 

char str[20];

str=”Доброе утро!”; //неверно

 

Для обработки строк используют специальный функции, которые хранятся в специальном заголовочном файле string.h, который должен быть подключен в разделе подключения заголовочных файлов.

В файле string.h определена функция присваивания текста строковой переменной. Эта функция имеет вид:

 

strcpy(переменная, текст);

 

Например:

 

char str[20];

strcpy(str,”Доброе утро!”);

 

С помощью данной функции можно присваивать не только текстовые константы, но и другие текстовые переменные. Например:

 

char str[20], str1[]=”пример”;

strcpy(str,str1);

 

Переменная str будет равна переменной str1. Но длина переменной str 20 символов (заполнено 7), а переменной str1 – 8.

При выполнении присваивания необходимо следить, чтобы длина присваиваемого текста не превышала длину переменной. В противном случае программа работает с ошибками.

Инициализация с помощью ввода

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

В принципе ввод текстовой переменной можно выполнить также, как и обычной числовой переменной. Например:

 

char str[20];

cout<<”Введите текст: ”;

cin>>str;

 

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

Доброе утро!

То в переменную str запишется только слово Доброе, а остальная часть, идущая через пробел, будет проигнорирована. Следовательно, функция cin подходит для ввода только строк, состоящих из одного слова: фамилии, имена, отчества и т.п.

Однако в языке С++ имеется функция, позволяющая организовать ввод текста с пробелами. Данный оператор находится в заголовочном файле iostream.h, который нужно будет подключить к программе. Сама функция имеет вид:

 

cin.getline(переменная,длина);

 

Например:

 

char str[20];

cout<<”Введите текст: ”;

cin.getline(str,20);

 

В данном случае в переменную str будут заноситься все вводимые символы до нажатия на клавишу enter. При этом если пользователь введет длинный текст, то в переменную запишется только 19 символов + символ null.

Смешанный алфавитно-цифровой ввод

Часть в программе приходится вводить данные разных типов. Для ввода чисел используют оператор cin<<, а для ввода текста – оператор cin.getline. использование этих операторов в одной программе имеет особенность, о которой нужно знать разработчику программ на С++.

Пусть имеются переменные вида:

 

int god; //год рождения человека

char fam[15]; //фамилия человека

 

Необходимо ввести значения этих переменных и вывести их на экран. Для этого код может иметь вид:

 

//вводим числовую переменную

cout<<"Введите табельный номер: ";

cin>>tab;

//вводим строковую переменную

cout<<"Введите фамилию: ";

cin.getline(fam,15);

//выводим результат ввода

cout<<"Вы ввели данные: табельный номер - "<<tab<<", фамилия - "<<fam;

 

Если запустить этот пример на выполнение, то окажется что после ввода табельного номера и нажатия на enter, программа не предложит ввести фамилию, а сразу выполнит оператор вывода результата, и на экране вы увидите текст вида:

 

Введите табельный номер: 1254

Введите фамилию: Вы ввели данные: табельный номер - 1254, фамилия -

 

Как видно из результата, фамилия не выводится. Это происходит по тому, что оператор cin<< считывает с клавиатуры в переменную tab введенное значение, но оставляет в памяти код клавиши enter. Следующий за ним оператор cin.getline считывает код клавиши enter и воспринимает его как окончание ввода текстовой переменной. В результате пользователь не может ввести текст, так как программа «думает», что пользователь сам нажал на enter, оставив текстовую переменную пустой. Такой эффект наблюдается постоянно, если после ввода числа происходит ввод текста.

Для решения данной проблемы необходимо очищать память после ввода чисел оператором cin<<. Для очистки используют команду вида:

 

cin.get();

 

Данная команда удаляет из памяти оставшийся код клавиши enter и следующий оператор ввода работает корректно. Для нашего примера код можно записать так:

 

//вводим числовую переменную

cout<<"Введите табельный номер: ";

cin>>tab;

//очищаем память от кода клавиши enter

cin.get();

//вводим строковую переменную

cout<<"Введите фамилию: ";

cin.getline(fam,15);

//выводим результат ввода

cout<<"Вы ввели данные: табельный номер - "<<tab<<", фамилия - "<<fam;

 

Очистку нужно проводить после оператора cin>> только в том случае, если следующим оператором ввода идет cin.getline. (начало)

 

3. Функции С++ для обработки строк

В заголовочном файле string.h описаны функции по работе со строками. Одна из них уже была рассмотрена выше: функция присваивания текста. Рассмотрим еще несколько из них.

Функции изменения регистра символов

Имеются функции, позволяющие заменить все буквы в тексте на строчные или прописные. Функции имеют вид:

 

strlwr(строка) – заменяет все символы (латинские) на строчные

strupr(строка) – заменяет все символы (латинские) на прописные

 

Данные функции не могут быть вызваны как отдельный оператор в программе. Они должны использоваться в операторе присваивания или других выражениях.

Например:

 

char str[20], str1[]="example", rez[20];

cout<<"Введите текст: ";

cin.getline(str,20);

//в переменной str заменим все символы на строчные

strcpy(str,strlwr(str));

//в переменной str1 заменим все символы на прописные

//результат запишем в переменную rez

strcpy(rez,strupr(str1));

//выведем полученные строки

cout<<"заменили все символы на строчные: "<<str<<endl;

cout<<"заменили все символы на прописные"<<rez<<endl;

 

Функция обращения текста

В С++ имеется функция, позволяющая записать исходный текст в обратном порядке. Функция имеет вид:

 

strrev(строка)

 

Данная функция не может быть вызвана в отдельной строке, но может использоваться в операторах присваивания, вывода или других выражении. Можно применять функцию как для латинских, так и для русских символов. Например, записать текст в переменной str в обратном порядке:

 

strcpy(str,strrev(str));

 

Содержимое переменной str выдать на экран в обратном порядке без изменения содержимого переменной.

 

cout<< strrev(str);

 

Функция объединения строк (конкатенация)

Если в программе нужно объединить два текста, то операция сложения строковых переменных недопустима. Для этого используется специальная функция:

 

strcat(строка1,строка2);

 

Функция складываем строку1 и строку2 и записывает результат в строку1.

Например:

 

char str[20]=”Доброе”, str1[]=” утро!”;

//в переменной str формируется фраза  «Доброе утро!»

strcat(str,str1);

 

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

Определение длины строки

Так как строка – это массив, то много задач для обработки строк приходится проводить в цикле, считывая строку посимвольно, как элемент массива. Для организации посимвольного считывания текста необходимо знать фактическую длину строки. Как известно, фактическая длина строки не всегда совпадает с той, которая указана при описании.

Для определения фактической длины строки используют функцию вида:

 

strlen(строка)

 

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

Например:

 

char str[20]=”Доброе”, str1[]=” утро!”;

//в переменной str формируется фраза  «Доброе утро!»

strcat(str,str1);

//на экран выводится фактическая длина строки str, равная 12 символов

cout<<strlen(str);

 

Длину строки можно использовать для проверки возможности объединения строк. Например:

 

char str[20], str1[20];

 

//вводим текст

cout<<”Введите первый текст: ”;

cin.getline(str,20);

cout<<”Введите второй текст: ”;

cin.getline(str1,20);

 

//проверяем может ли str поместить в себе str+str1

if (strlen(str)+strlen(str1)<=20)

{

//если может, то объединяем тексты

strcat(str,str1);

//и выводим результат

cout<<str;

}

else

//если нет, то выдаем сообщение об ошибке

cout<<”Текст слишком большой для объединения”<<endl;

 

Функция strlen часто используется для организации циклов посимвольной обработки строк.

 

Сравнение строк

В С++ можно сравнивать строки целиком и части строк.

Для сравнения строк целиком используют функции:

 

//сравнивает строки с учетом регистра символов

strcmp(строка1, строка2)

//сравнения строк без учета регистров символов

stricmp(строка1,строка2)

 

Данная функция сравнивает строки и возвращает в программу одно из значений:

 

Например:

 

char str[]=aaa, str1[]=”AAA”;

int k;

// запоминаем результат сравнения

k=strcmp(str,str1);

if (k>0)

     cout<<”Строка 1 больше строки 2”;

else if (k==0)

     cout<<”Строки равны между собой”;

else

     cout<<”Строка 1 меньше строки 2”;

 

В данном примере используется функция сравнения строк strcmp, которая учитывает регистр символов. То есть в нашем случае на экран будет выдано сообщение:

Строка1 больше строки2

Это происходит потому, что строчные и заглавные символы имеют разный ASCII-код. Строчные символы имеют больший код, чем заглавные.

Запишем предыдущий пример по-другому:

 

char str[]=aaa, str1[]=”AAA”;

int k;

// запоминаем результат сравнения

k=stricmp(str,str1);

if (k>0)

     cout<<”Строка 1 больше строки 2”;

else if (k==0)

     cout<<”Строки равны между собой”;

else

     cout<<”Строка 1 меньше строки 2”;

 

В данном примере используется функция сравнения строк stricmp. Данная функция сравнивает строки без учета регистра символов. То есть строчные и заглавные символы воспринимаются как одинаковые. В нашем случае на экран будет выдано сообщение:

Строки равны между собой

Для сравнения части строк в С++ используются функции:

 

//сравнение части строки с учетом регистра

strncmp(строка1,строка2,n)

//сравнение части строки без учета регистра

strnicmp(строка1,строка2,n)

 

Функции сравнивают n символов строки1 с n символами строки2. Функции также возвращают одно из трех значений в зависимости от результата сравнения:

 

Например:

 

char str[]=”Простокваша”, str1[]=”просто”;

int k;

//запоминаем результат сравнения

k=strncmp(str,str1,6);

if (k>0)

     cout<<”Строка 1 больше строки 2”;

else if (k==0)

     cout<<”Строки равны между собой”;

else

     cout<<”Строка 1 меньше строки 2”;

 

На экран будет выдано сообщение:

Строка1 больше строки2

так как слово «просто» больше слова «Просто».

 

Если пример записать так:

 

char str[]=”Простокваша”, str1[]=”просто”;

int k;

// запоминаем результат сравнения

k=strnicmp(str,str1,6);

if (k>0)

     cout<<”Строка 1 больше строки 2”;

else if (k==0)

     cout<<”Строки равны между собой”;

else

     cout<<”Строка 1 меньше строки 2”;

 

то на экран будет выдано сообщение:

Строки равны между собой

так как слова «просто» и «Просто» сравниваются без учета регистра символов. (начало)

 

4. Посимвольная обработка строк

Много программ по работе с текстом производят посимвольную обработку строки. В этом случае строка рассматривается как массив и обрабатывается с помощью операторов цикла. Как и в массиве, первый символ строки имеет номер 0, а последний определяется по формуле strlen(строка)-1.

Для понимания принципов посимвольной обработки рассмотрим несколько примеров.

 

Пример. Пусть имеется текст. Подсчитать сколько слов заканчивается на окончание «ое». Предполагается, что слова могут разделяться только пробелами.

Признаком окончания «ое» является буквосочетание вида: «ое(пробел)». Будем подсчитывать количество таких сочетаний. Однако, если последнее слово будет заканчиваться на это сочетание, то оно подсчитано не будет, так как после последнего слова нет пробела. Для решения этой проблемы мы программно добавим в конец текста пробел и обработаем текст с этой «добавкой».

 

//подключаем заголовочные файлы

#include <iostream.h>

#include <string.h>

int main()

{

system("chcp 1251>nul");   

//строка str - исходный текст

//строка buf - промежуточная строка     

char str[20], buf[20];

int i,kol;

//вводим текст     

cout<<"Введите текст:";

cin.getline(str,20);

//копируем строку в буфер     

strcpy(buf,str);

//добавляем к буферу пробел в конец     

strcat(buf," ");

//организуем цикл по всему тексту     

for(kol=0,i=0; i<=strlen(buf)-3; i++)

{

     //если встретилось сочетание "ое(пробел)"   

     if (buf[i]=='о' && buf[i+1]=='е' && buf[i+2]==' ')

     //увеличиваем количество на 1     

     kol++;

}

 

//выдаем результат на экран     

cout<<"Количество слов, заканчивающихся на \'ое\' = "<<kol<<endl;

system("pause");

return 0;

}

 

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

 

Примечание: решать задачу будем так ­­– перепишем в буферную строку все символы из исходной строки, кроме лишних пробелов.

 

//подключаем заголовочные файлы

#include <iostream.h>

#include <conio.h>

#include <string.h>

int main()

{

system("chcp 1251>nul");

//строка str - исходный текст

//строка buf - промежуточная строка     

char str[20], buf[20];

int i,j;

//вводим текст     

cout<<"Введите текст:";

cin.getline(str,20);

//организуем цикл по всему тексту     

for(j=0,i=0; i<=strlen(str)-1; i++)

//если текущий символ не пробел

//или текущий символ пробел, а следующий не пробел

if (str[i]!=' ' || (str[i]==' ' && str[i+1]!=' '))

{

//переписываем этот символ в буферную строку

buf[j]=str[i];

//увеличиваем счетчик символов в буферной строке на 1

j++;

}

//в буферной строке ставим метку окончания строки

buf[j]='\0';

//копируем полученную строку в исходную переменную

strcpy(str,buf);

//выводим результат на экран

cout<<str<<endl;

system("pause");

return 0;

} (начало)