Будьте осторожны при явной инициализации строки. Так, оператор

МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РФ

СТАРООСКОЛЬСКИЙ ТЕХНОЛОГИЧЕСКИЙ ИНСТИТУТ им. А.А. Угарова

(ФИЛИАЛ) ФЕДЕРАЛЬНОГО ГОСУДАРСТВЕННОГО АВТОНОМНОГО ОБРАЗОВАТЕЛЬНОГО УЧРЕЖДЕНИЯ ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ

НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ ТЕХНОЛОГИЧЕСКИЙ УНИВЕРСИТЕТ

«МИСиС»

о

Козырь О.Ф.

ПРОГРАММИРОВАНИЕ И АЛГОРИТМИЗАЦИЯ

Методическое пособие к выполнению

Домашних работ

Для студентов специальностей

140604 – Электропривод и автоматика промышленных установок и технологических комплексов

220301 – Автоматизация технологических процессов и производств

( все формы обучения)

Квалификация (степень) выпускника

Бакалавр

Одобрено редакционно-издательским советом института

Старый Оскол

Год

УДК 004

ББК 32.97

К 593

Рецензент:

Профессор кафедры системотехники Харьковского национального университета радиоэлектроники, к.т.н. С.И. Чайников

Козырь О.Ф. Программирование и алгоритмизация. Методическое пособие к выполнению домашних заданий. Старый Оскол. СТИ МИСиС, 2013г, - 24с.

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

© Козырь О.Ф.

© СТИ МИСиС

Содержание.

Введение……………………………………………………………………….……….

Домашняя работа №1. Решение задач с обработкой строк…………………………

Домашняя работа №2. Решение задач с использованием файлов………………..…

Список рекомендуемой литературы…………………………………………………..

Введение

Настоящий цикл домашних заданий разработан по дисциплине "Программирование и алгоритмизация" для студентов очной, очно-заочной и заочной форм обучения специальностей "Автоматизация технологических процессов и производств" и «Электропривод и автоматика промышленных установок и технологических комплексов». Лабораторные работы проводятся на ПК с использованием средств TurboС V3.0 или BuilderC++ V5.0 (или V6.0) или любого другого транслятора языка С++.

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

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

Тематика домашних работ дополняет материал лабораторных работ и помогает студентам освоить приемы работы с файлами и строками средствами языка С++. Для закрепления изученного материала и самопроверки студентами полученных знаний по каждой теме дается перечень контрольных вопросов.

Общие требования к отчетам по домашним работам таков: отчет должен содержать:

- титульный лист,

- тему выполняемой работы,

- цель и задание,

- текст программы (или несколько текстов программ), снабженный комментариями,

- результаты работы программы (скрин-шот).

Домашняя работа № 1.

РЕШЕНИЕ ЗАДАЧ С ОБРАБОТКОЙ СТРОК

Цель работы

Изучить и освоить методы составления программ на языке С++ с использованием строк.

Подготовка к работе

Необходимо знать основные особенности представления символов и строковых литералов в языке С++ [лекции 10, 14], способы их обработки, основные функции используемые для сравнения, копирования строк, вырезания и поиска подстроки (символа) в строке.

3. Теоретическая часть:

Для представления текстовой информации в языке С++ используются символы (константы), символьные переменные и строки (строковые константы), для которых в языке С++ не введено отдельного типа в отличие от некоторых других языков программирования. Строка– это последовательность символов, заключенная в двойные кавычки (" ").

Размещая строку в памяти, транслятор автоматически добавляет в ее конце символ '\0' (нулевой символ или нулевой байт, который является признаком конца строки). В записи строки может быть и один символ: "А" (заключен в двойные кавычки), однако, в отличие от символьной константы 'А' (используются апострофы), длина строки "А"равна 2 байтам.

В языке С++ строка – это пронумерованная последовательность символов (массив символов), она всегда имеет тип char[]. Все символы строки нумеруются, начиная с нуля. Символ конца строки также нумеруется – ему соответствует наибольший из номеров. Таким образом, строка считывается значением типа "массив символов". Количество элементов в таком массиве на 1 больше, чем изображение соответствующей строки, так как в конец строки добавлен нулевой символ '\0' (рис. 1).

Будьте осторожны при явной инициализации строки. Так, оператор - student2.ru


Рис. 1. Представление строки и символа

Символьная строка в программном коде может располагаться на нескольких строках. Для переноса используется символ '\' с последующим нажатием клавиши ввод. Символ '\' игнорируется компилятором, и следующая строка считается продолжением предыдущей.

В С++ есть два вида строк:

- С-строки (символьные);

- класс стандартной библиотеки С++ string.

С-строка – это массив символов, завершающийся символом с кодом 0 (нуль-терминатором). Класс string более безопасен в использовании, но и более ресурсоемок, чем С-строки. Исходя из этого, строки можно обрабатывать двумя способами:

- как массив;

- используя строковые функции, описания которых находятся в заголовочном файле string.h (класс cstring).

Каждый из способов имеет свои достоинства и недостатки.

Порядок работы со строкой:

1) Описание строки (выделение памяти);

2) Инициализация (ввод) строки;

3) Обработка строк.

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

Описание строк.

Используется описатель:

char <имя_строки>[<длина>]

Длина динамической строки может быть выражением, не динамической строки – константным выражением (чаще всего константой):

const int len_s = 80;

char s[len_s]; //хранится 79 символов + 0-терминатор

При задании строки необходимо учитывать 0-терминатор.

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

char *p = new char[m]; // p – указатель на char

char *q = (char *)malloc(m * sizeof(char));

Инициализация (ввод) строк.

1) явная инициализация:

Строки при описании можно явно инициализировать строковыми константами:

char a[100] = “Строковая константа”;

или инициализировать как массив символов:

char str[10]={‘s’, ‘t’, ‘r’, ‘o’, ‘k’, ‘a’};

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

char a[] = “Строковая константа”; // выделено под 20 символов

Будьте осторожны при явной инициализации строки. Так, оператор

char *str = “Строковая константа”;

использовать некорректно, так как он создает не строковую переменную, а указатель на строковую константу, изменить которую невозможно.

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

2) интерактивный ввод строки (с использованием различных операторов ввода).

- для ввода строки, состоящей из одного слова (ввод до первого пробельного символа: пробела, знака табуляции, символа перевода строки) используется cin:

const int n = 80;

char s[n];

cin >> s;

- для ввода строки, состоящей из нескольких слов, используются методы getline или get класса istream, объектом которого является cin. Для вызова метода после имени объекта ставится точка, а затем пишется имя метода:

const int n = 80;

char s[n];

cin.getline(s,n); cout << s << endl;

cin.get(s,n); cout << s << endl;

Метод getline считывает из входного потока (n-1) символов или менее (если символ перевода строки встретиться ранее) и записывает их в строковую переменную s. Символ перевода строки (он же ‘\n’, появляется во входном потоке при нажатии клавиши Enter) также сичтывается (удаляется) из входного потока, вместо него размещается завершающий нуль-терминатор.

Метод get работает аналогично, но оставляет в потоке символ перевода строки. В строковую переменную добавляется завершающий нуль-терминатор. Нельзя обращаться к методу get с двумя аргументами два раза подряд, не удалив ‘\n’ из входного потока. Это возможно вызовом метода get без параметров:

cin.get(s,n); cout << s << endl;

cin.get(); // убрали ‘\n’

cin.get(s,n); cout << s << endl;

- для ввода нескольких строк удобно использовать метод getline в заголовке цикла:

const int n = 80;

char s[n];

while (cin.getline(s,n)) {

cout << s << endl;

… }

- для ввода строки (до первого пробельного символа) можно использовать функцию scanf(),задав спецификацию формата ‘%s’:

const int n = 80;

char s[n];

scanf(“%s”, s); printf(“%s”, s);

- чтобы ввести строку из нескольких слов используется спецификация ‘%c’с указанием максимального количества вводимых символов:

scanf(“%80с”, s);

- для ввода строк используются также специальная функция gets():

const int n = 80;

char s[n];

gets(s);

Функция gets(s) читает символы с клавиатуры до появления символа новой строки и помещает их в строку s (сам символ в строку не включается, вместо него в строку заносится нуль-символ). Функция возвращает указатель на строку s, а в случае возникновения ошибки или конца файла – NULL.

Вывод строки на экран.

- для вывода может использоваться команда стандартного (потокового) вывода “<<” библиотеки С++:

const int n = 80;

char s[n];

cin >> s; cout << s << endl;

- при выводе с помощью функции printf(), можно задать количество позиций, отводимых под строку; строка при этом выравнивается по правому краю отведенного поля:

const int n = 80;

char s[n];

scanf(“%s”, s); printf(“%s”, s);

printf(“%80s”, s); // с заданием количества позиций в строке

- функция puts(s) выводит строку s на стандартное устройство вывода, заменяя завершающий нуль-терминатор символом новой строки. Возвращает неотрицательное значение при успехе или EOF (признак конца файла) при ошибке:

const int n = 80;

char s[n];

gets(s); puts(s);

Рассмотрим примеры использования команд ввода-вывода строк в различных компиляторах С++:

1) Использование функций gets() и puts():

Пример 1:

#include "stdafx.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]){ char s[20]; gets(s); puts(s); system("pause"); return 0;}

Результат выполнения программы: при вводе строки "123 456 789" чтение данных осуществляется побайтно до символа '\n', то есть в s занесется строка "123 456 789\0" (управляющая последовательность '\0' на экран не выводится, а является признаком конца строки). При выводе строки функция puts() возвращает в конце строки дополнительно один символ '\n', следовательно, будет выведена строка "123 456 789\n" (управляющая последовательность '\n' на экран не выводится, а осуществляет перевод курсора на новую строку).

Пример 2. Вычислите длину введенной строки.

#include "stdafx.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]){ char st[100]; int i=0; puts("Введите строку:"); gets(st); while(st[i++]); printf("Длина введенной строки = %i\n",i-1); system("pause"); return 0;

2) Использование стандартного ввода-вывода:

Пример 3:

#include "stdafx.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]){ char s[20]; cin>>s; //ввод строки из стандартного потока cout<<s; //вывод строки в стандартный поток system("pause"); return 0;}

Результат выполнения программы: при вводе строки "123 456 789" чтение данных осуществляется побайтно до первого пробела, то есть в s занесется только первое слово строки "123\0", следовательно, выведется: "123".

Пример 4. Введите слово и замените в нем все вхождения заглавной латинской 'A'на малую латинскую 'a'. Выведите слово после редактирования.

#include "stdafx.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]){ char st[80]; int i; cout << "\nВведите слово: "; cin >> st; for(i=0;st[i]!='\0';i++) if (st[i]=='A') st[i]='a'; cout << "\nСлово после редактирования: "<< st; system("pause"); return 0;}

3) Использование форматированного ввода-вывода:

Пример 5:

#include "stdafx.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]){ char s[20]; scanf("%s",s); //для строк не используется обращение по адресу & printf("%s",s); system("pause"); return 0;}

Результат выполнения программы: при вводе строки "123 456 789", чтение данных осуществляется побайтно до первого пробела, то есть в строку s занесется только первое слово строки "123\0", следовательно, выведется: "123". Так как s– имя символьного массива, то есть адрес его начального элемента, операция & в функции scanf для строк не используется.

Пример 6. Записать введенную строку символов в обратном порядке.

#include "stdafx.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]){ char st[80]; char temp; int i,len=0; printf("\nВведите строку > "); scanf("%s",st); while (st[len++]); //вычисление длины строки len-=2;//поправка на символ конца строки и нумерацию с нуля for(i=0;i<len;i++,len--){ temp=st[i]; //обмен символов st[i]=st[len]; st[len]=temp; } printf("\nПолученная строка > %s",st); system("pause"); return 0;}

Обработка строк.

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

В программах, в зависимости от типа, вызовы функций для работы со строками задаются в виде:

ИмяФ(СписокАргументов);

или

ИмяПерем=ИмяФ(СписокАргументов);

где ИмяФ – имя функции;

СписокАргументов – список аргументов, передаваемых в тело функции;

ИмяПерем – идентификатор соответствующего типа.

Например:

y=strlen(st); /*переменной y присвоить значение длины строки st*/

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

· Функции, работающие с регистрами, распространяются только на латиницу.

· В С++ некоторые параметры функций обработки символов принадлежат типу int (unsigned), поэтому, если число станет больше 128 (255), функция будет работать некорректно.

· Перед первым обращением к строке она должна быть объявлена и проинициализирована. Во многих случаях в качестве начального значения строки необходимо бывает задать пустую строку. Такую инициализацию можно выполнить с помощью вызова функции

strcpy(s, "");

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

*s=0;

Кроме того пустую строку можно инициализировать

char s[10]="";

или

char s[10]="\0";,

но при этом размер строки должен быть задан.

· Функции копирования (кроме strncpy()) не проверяют длину строки. Размер строки-приемника должен быть больше, чем размер источника на 1 символ (для символа '\0' ).

Основные функции работы со строками приведем в таблице 1.

Таблица 1. Основные функции работы со строками

Функции для работы со строками – файл stdlib.h
Функция Прототип Краткое описание действий
atof double atof (const char *str); преобразует строку str в вещественное число типа double
atoi int atoi (const char *str); преобразует строку str в целое число типа int
atol long atol (const char *str); преобразует строку str в целое число типа long
itoa char *itoa (int v, char *str, int baz); преобразует целое v в строку str. При изображении числа используется основание baz (2<=baz<=36). Для отрицательного числа и baz =10 первый символ "минус" (–).
ltoa char *ltoa (long v, char *str, int baz); преобразует длинное целое v в строку str. При изображении числа используется основание baz (2<=baz<=36).
ultoa char *ultoa (unsigned long v, char *str, int baz); преобразует беззнаковое длинное целое v в строку str
Функции для работы со строками – файл string.h
Функция Прототип Краткое описание действий
strcat char *strcat (char *sp, const char *si); приписывает строку si к строке sp (конкатенация строк)
strchr char *strchr (const char *str, int c); ищет в строке str первое вхождение символа с
strcmp int strcmp (const char *str1, const char *str2); сравнивает строки str1 и str2. Результат отрицателен, если str1<str2; равен нулю, если str1==str2, и положителен, если str1>str2 (сравнение беззнаковое)
strcpy char *strcpy (char *sp, const char *si); копирует байты строки si в строку sp
strcspn int strcspn (const char *str1, const char *str2); определяет длину первого сегмента строки str1, содержащего символы, не входящие во множество символов строки str2
strdup char *strdup (const char *str); выделяет память и переносит в нее копию строки str
strlen unsignedstrlen (const char *str); вычисляет длину строки str
strlwr char *strlwr (char *str); преобразует буквы верхнего регистра в строке в соответствующие буквы нижнего регистра
strncat char *strncat (char *sp, const char *si, int kol); приписывает kol символов строки si к строке sp (конкатенация)
strncmp int strncmp (const char *str1, const char *str2, int kol); сравнивает части строк str1 и str2, причем рассматриваются первые kol символов. Результат отрицателен, если str1<str2 ; равен нулю, если str1==str2, и положителен, если str1>str2
strncpy char *strncpy (char *sp, const char *si, int kol); копирует kol символов строки si в строку sp ("хвост" отбрасывается или дополняется пробелами)
strnicmp int strnicmp (char *str1, const char *str2, int kol); сравнивает не более kol символов строки str1 и строки str2, не делая различия регистров (см. функцию strncmp )
strnset char *strnset (char *str, int c, int kol); заменяет первые kol символов строки str символом c
strpbrk char *strpbrk (const char *str1, const char *str2); ищет в строке str1 первое появление любого из множества символов, входящих в строку str2
strrchr char *strrchr (const char *str, int c); ищет в строке str последнее вхождение символа с
strset char *strset (char *str, int c); заполняет строку str заданным символом c
strspn int strspn (const char *str1, const char *str2); определяет длину первого сегмента строки str1, содержащего только символы, из множества символов строки str2
strstr char *strstr (const char *str1, const char *str2); ищет в строке str1 подстроку str2. Возвращает указатель на тот элемент в строке str1, с которого начинается подстрока str2
strtod double strtod (const char *str, char **endptr); преобразует символьную константу strв число двойной точности. Если endptr не равен NULL, то *endptr возвращается как указатель на символ, при достижении которого прекращено чтение строки str
strtok char *strtok (char *str1, const char *str2); ищет в строке str1 лексемы, выделенные символами из второй строки
strtol long strtol (const char *str, char **endptr, int baz); преобразует символьную константу str к значению "длинное число" с основанием baz (2<=baz<=36). Если endptr не равен NULL, то *endptr возвращается как указатель на символ, при достижении которого прекращено чтение строки str
strupr char *strupr (char *str); преобразует буквы нижнего регистра в строке str в буквы верхнего регистра

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

1) присваивание выполняется с помощью функций стандартной библиотеки или посимвольно «вручную». Например, чтобы присвоить строке р строку а можно воспользоваться функциями strcpy() или strnсpy(). При вызове функции strncpy() следует помнить, что, если длина копируемой строки превосходит параметр kol, то строка-получатель не будет завершена символом '\0'. В этом случае такой символ надо дописывать в конец строки вручную.

char a[100] = “Строковая константа”;

char *p = new char [m];

strcpy(p,a);// копирование всех символов строки а + «\0» в р

strncpy(p,a,strlen(a)+1); //копирование указанного количества

//(strlen(a)+1)символов строки а в строку р

Для использования этих функций следует подключить заголовочный файл <string.h>.

Функция strlen(a) возвращает фактическую длину строки а не включая нуль-символ.

2) для преобразования строки в целое число используется функция atoi(), в длинное целое – atol(), в вещественное число с двойной точностью – atof().

Пример применения функций преобразования:

char a[]= “10) Рост – 162 см, вес – 59.5 кг”;

int num;

long rost;

double ves;

num = atoi(a);

rost = atol(&a[11]);

ves = atof(&a[25]);

cout << num << ‘ ‘ << rost << ‘ ‘ << ves;

3) Операция вычисления размера (в байтах) sizeof действует для объектов символьного типа и строк.

// Определение размера строк#include "stdafx.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]) { char s1[10]="string1"; int k=sizeof(s1); cout<<s1<<"\t"<<k<<"\n"; char s2[]="string2"; k=sizeof(s2); cout<<s2<<"\t"<<k<<"\n"; char s3[]={'s','t','r','i','n','g','3','\0'}; /*окончание строки '\0' следует соблюдать, формируя в программах строки из отдельных символов*/ k=sizeof(s3); cout<<s3<<"\t"<<k<<"\n"; char *s4="string4"; //указатель на строку, ее нельзя изменить k=sizeof(s4); cout<<s4<<"\t"<<k<<"\n"; system("pause"); return 0;}

Результат выполнения программы:

string1 10 – выделено 10 байтов, в том числе под '\0'

string2 8 – выделено 8 байтов (7 + 1 байт под '\0' )

string3 8 – выделено 8 байтов (7 + 1 байт под '\0' )

4) Сравнение строк с помощью функции strcmp() осуществляется побайтово в лексикографическом порядке, то есть в порядке следования соответствующих байтов строк в таблице кодировки. Именно поэтому значения элементов в строках зависят от регистра.

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

Пример 1.

//Программа демонстрирует работу функций из файла stdlib.h #include "stdafx.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]){ char sv[]="23.547", si[]="1234", sl[]="-112424", st1[15],st2[25],st3[15]; double v; int i; long l,t=457821; v=atof(sv); printf("Преобразование строки в вещественное число = %f\n",v); i=atoi(si); printf("Преобразование строки в целое число = %d\n",i); l=atol(sl); printf("Преобразование строки в длинное целое число = %ld\n",l); printf("Преобразование длинного целого числа в строку = %s\n", ultoa(t,st1,10)); printf("Преобразование длинного целого числа в строку = %s\n", ultoa(t,st2,2)); printf("Преобразование длинного целого числа в строку = %s\n", ultoa(t,st3,16)); system("pause"); return 0;}

Пример 2.

//Программа демонстрирует работу функций из файла string.h #include "stdafx.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]){ char st[50],sp[100],str[]="", si[]="qwerty", sl[]="qwerty", sw[]="qwertyu"; int len=0, sravn1, sravn2, sravn3, kol=5; printf("Введите строку: "); gets(st); len=strlen(st); printf("Длина строки = %d\n",len); printf("Конкатенация строк: %s\n",strcat(st,"12cdb")); sravn1=strcmp(si,sl); printf("Сравнение строк: %s==%s результат %d\n", si, sl, sravn1); sravn2=strcmp(si,sw); printf("Сравнение строк: %s<%s результат %d\n",si, sw, sravn2); sravn3=strcmp(sw,si); printf("Сравнение строк: %s>%s результат %d\n", sw, si, sravn3); printf("Копирование байтов: %s\n", strcpy(sp,st)); printf("Преобразование букв нижнего регистра в верхний: %s\n", strupr(st)); printf("Преобразование букв верхнего регистра в нижний: %s\n", strlwr(st)); printf("Копирование %d символов в другую строку: %s\n", kol,strncpy(str,st,kol)); printf("Поиск в строке первого появления символа из другой строки: %s\n",strpbrk(st,si)); printf("Поиск в строке последнее вхождение заданного символа: %s\n",strrchr(st,'t')); system("pause"); return 0;}

Пример 3.

//Поиск множества неповторяющихся символов строки#include "stdafx.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]){ char st[80]; int i,j,flag,len; printf("Введите строку: "); gets(st); len=strlen(st); //длина строки printf("Неповторяющиеся символы строки образуют множество: "); for (i=0;i<len;i++){ flag=0; //флаг проверки на совпадение for (j=0;j<i;j++) //сравнение символа с предыдущими if (st[i]==st[j]) flag=1; if (flag==0)printf("%c",st[i]); } system("pause"); return 0;}

Пример 4.

/*Удаление лишних левых и внутренних пробелов в строке при выводе*/#include "stdafx.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]){ char st[80]; int i=0,len; printf("Введите строку: "); gets(st); len=strlen(st);//длина строки printf("Преобразованная строка: "); //Удалены лишние пробелы слева while (st[i++]==' '); //Удалены лишние пробелы внутри строки for (--i;i<len;i++) if ((st[i]!=' ')||(st[i+1]!=' ')) printf("%c",st[i]); //если рядом два пробела system("pause"); return 0;

}

Строки и указатели

Строки в языке С++ представляют собой массив символов. Поскольку имя массива без индексов является указателем на первый элемент этого массива, то при использовании функций обработки строк им будут передаваться не сами строки, а указатели на них. Так как все строки в языке С++ заканчиваются нулевым символом, который имеет значение <ложь>, то условие в операторе while(*str) будет истинным до тех пор, пока программа не достигнет конца строки.

При разработке функций для работы со строками в большинстве случаев целесообразно применять указатели. Указатель на строку – адрес начала расположения строки в памяти.

Адрес строки– это указатель на блок непрерывной области памяти, с которого начинает располагаться массив символов.

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

Строки передаются в функции в качестве параметров как массивы символов или как указатели типа char.

Обращение к конкретному элементу строки можно осуществить посредством адресации индексированного имени строки.

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

С помощью указателей на константы можно защитить строку от изменений.

Копирование строк с помощью указателей осуществляется через объявление нового указателя, адресующего область памяти, занимаемую строкой или подстрокой.

on_load_lecture();

Приведем примеры фрагментов программ:

/*Пример пользовательской функции копирования строки s2 в s1*/

char * strcpy_my (char *s1, char *s2){

char *ptrs1 = s1;

//указатель инициализирован на начало строки

while ((*s1++ = *s2++) != 0);

return ptrs1; //возвращается указатель на строку s1

}

Следующий пример демонстрирует, что использование нулевого ограничителя («нуль-терминатора») упрощает различные операции над строками.

/*Пример пользовательской функции конкатенации*/

char * strcat_my (char *s1, char *s2) {

char *p1, *p2;

p1 = s1; p2 = s2;

while ( *p1 != '\0') p1++; //найти конец 1-ой строки.

//или while ( *p1) p1++;

while ((*p1 = *p2) != 0) {

/*копировать строку р2, пока не будет скопирован нулевой

Ограничитель*/

p1++;

p2++; //Передвинуть указатели к следующему байту

} /* или заменить весь цикл: while (( *p1++ = *p2++) != 0);

вместо «!=0» можно записать «!=NULL» /*

return s1;

}

Пример 1.

/*Демонстрация работы с указателями и с функциями для обработки строк*/

#include "stdafx.h"

#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[]){

char string[100], temp[100], *result, simvol;

int numresult, res;

/*создает строку "computer program" посредством

использования strcpy и strcat*/

strcpy(string, "computer");

result = strcat(string," program");

printf("1) создали строку\n%s\n",result);

/*находит строку, в которой первый раз обнаружено 'a'*/

simvol='a';

result = strchr(string,simvol);

printf("2) находим в строке первое вхождение символа \'%c\'\n

%s\n",simvol,result);

/* создает копию строки */

result = strcpy(temp,string);

printf("3) создали копию строку\n%s\n",result);

/* находит "a","b","c" в строке */

strcpy(string,"xyzabbc");

res = strcspn(string,"abc");

printf("4) определяем длину заданного сегмента \n%d\n",res);

/*создает новый указатель на строку для дублирования

строки*/

result = strdup(string);

printf("5) создали новый указатель на строку \n%s\n",result);

system("pause");

return 0;

}

В предыдущих примерах рассматривалось присваивание указателю адреса только первого элемента символьного массива. Однако это можно делать и с адресом любого отдельного элемента массива путем добавления символа '&' к индексированному имени. Особенно удобно пользоваться этим правилом при выделении подстроки. Например, следующая программа выводит на экран часть введенной строки после первого пробела:

Пример 2.

/*Вывести на экран часть строки после первого пробела*/

#include "stdafx.h"

#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[]){

char s[80], *p;

int i;

printf("ввести строку: ");

gets(s);

/*найти первый пробел или конец строки*/

for(i=0; s[i] && s[i]!=' '; i++);

p = &s[i];

printf(p);

system("pause");

return 0;

}

В этой программе указатель p будет указывать либо на пробел, если он есть, либо на ноль, если в строке нет пробелов. Если p указывает на пробел, то программа выведет на экран его и затем остаток строки. Например, если ввести фразу <язык программирования С++>, функция printf() напечатает сначала пробел и затем <программирования С++>. Если p укажет на ноль, то на экран ничего не выводится.

Пример 3:

//Выводит каждое отдельное слово и подсчитывает его длину

#include "stdafx.h"

#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[]){

char text[100],*p, *razd=" .,";

int dlina;

puts ("Введите текст ");

gets(text);

p=strtok(text,razd); // Выделение первого слова текста

while (p) { // Пока можно выделить слово

dlina=strlen(p); // Определение длины слова

cout << "\n слово "<< p << " длина = " << dlina <<"\n";

p=strtok(NULL,razd);

//Выделение второго, третьего, и т.д. слов

}

system("pause");

return 0;

}

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

· При передаче строки как параметра функции не указывается длина, так как ограничителем является символ конца строки.

· Строки передаются в функции в качестве параметров как массивы символов или как указатели типа char.

· При побайтовом копировании строки или ее подстроки без использования стандартных функций формируемую строку следует завершить, дописав символ конца строки. В противном случае строка не воспринимается как единое целое, а при выходе за ее границы доступными становятся байты, содержащие "мусор", то есть непредсказуемую информацию.

· Обращение к строкам через указатели позволяет вносить и сохранять изменения, записанные в адресуемой области памяти. Для недопущения изменений в строке указатель на константу можно объявить с лексемой const следующим образом: const char *p;.

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

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

Например:

char text[50]="Язык программирования";

char *p=text, *pp;

//объявление и инициализация указателя р адресом строки text

pp=p;

//указатель рр адресует ту же строку text

Адресация на тот же участок памяти объясняется:

1) неэффективностью повторного хранения уже имеющихся данных,

2) относительной программной трудоемкостью копирования байтов,

3) для хранения адреса строки требуется гораздо меньше места, чем для самой строки.

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

Работа с символами.

Для хранения отдельных символов используются переменные типа char. Их ввод – вывод также может выполняться как с помощью классов ввода-вывода, так и с помощью функций библиотеки.

При использовании классов ввод – вывод осуществляется с помощью операций помещения в поток >> и извлечения потока <<, так и методов get() и get(char):

#include <iostream.h>

int main(){

char c, d, e;

cin >> c; // так ввести пробел нельзя

cin >> d >> e;

cout << c << d << e;

c = cin.get(); //возвращает код символа в с

cin.get(d); cin.get(e); // запись символов d и е в переменные

cout << c << e;

return 0;}

В заголовочном файле <stdio.h> определены функции для стандартного ввода (getchar()) и вывода (putchar()):

getchar()– функция (без параметров) используется для ввода одиночного символа из входного потока. Она возвращает 1 байт информации (символ) в виде значения типа int. Это сделано для распознавания ситуации, когда при чтении будет достигнут конец файла.

putchar(ch)– функция используется для вывода одиночного символа, то есть помещает в стандартный выходной поток символ ch. Аргументом функции вывода может быть одиночный символ (включая знаки, представляемые управляющими последовательностями), переменная или функция, значением которой является одиночный символ.

char c, d;

c = getchar();

d = getchar(); putchar(d);

Например:

/*Программа считывает из входного потока один символ, а затем выводит его на экран*/#include "stdafx.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]){ char ch; ch=getchar(); putchar(ch); system("pause"); return 0;}

Пример следующий. Введите предложение, в конце которого стоит точка, и подсчитайте общее количество символов, отличных от пробела (не считая точки).

#include "stdafx.h"#include <iostream>using namespace std;int _tmain(int argc, _TCHAR* argv[]){ char z; //z - вводимый символ int k; //k - количество значащих символов printf("Напишите предложение с точкой в конце:\n"); for (k=0; (z=getchar())!='.';) /*выражение z=getchar() заключено в скобки, так как операция присваивания имеет более низкий ранг, чем операция сравнения*/ if (z!=' ') k++; printf("\nКоличество символов=%d",k); system("pause"); return 0;}

Результат выполнения программы:

Напишите предложение с точкой в конце:1 2 3 4 5 6 7 8 9 0.Количество символов=10

Теперь, когда мы познакомились с основными приемами работы со строками и символами, рассмотрим более сложные примеры программ:

1. Поиск подстроки.

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

Так как переносы отсутствуют, то текст умещается в одной строке. Для ее хранения выделим строковую переменную длиной в 81 символ (дополнительный для нуль-символа).

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

Результатом работы программы является сообщение либо о наличии заданной последовательности, либо об ее отсутствии.

#include <string.h>

int main(){

const int len = 81; // длина строки и последовательности

char word[len], line[len]; // описание строки и

// последовательности

cout<<”Введите строку не более 80 симв”;

cin.getline(line, len);

cout<<”Введите слово для поиска”; cin >> word;

if (strstr(line, word)) // поиск последовательности в строке

cout<<”Присутствует!” << endl;

else

cout<<”Отсутствует!” << endl;

return 0; }

2. Подсчет количества вхождений слова в строку.

Написать программу, определяющую сколько раз встретилось заданное слово в строке. Длина строки не превышает 80 символов. Текст не содержит переносов слов.

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

Слово может находиться либо в начале строки, либо после разделителя или знака пунктуации. Определим слово схематически:

слово =

{начало строки | знак пунктуации | разделитель}

символы, составляющие слово

{конец строки | знак пунктуации | разделитель}

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

При обнаружении совпадения с символами, составляющими слово, требуется определить, является ли оно самостоятельным словом или частью другого слова (например, искомое слово «кот» может содержаться в словах «котенок», «трикотаж», «трескотня» и т.д.). Следовательно, нужно проверить символ, стоящий после слова, и если слово не находится в начале строки, то и символ перед ним. Эти символы проверяются на принадлежность множеству знаков пунктуации и разделителей.

. . .

int main(){

const int len = 81;

char word[len], line[len];

cout<<”Введите строку не более 80 симв”;

cin.getline(line, len);

cout<<”Введите слово для поиска”; cin >> word;

int l_word = strlen(word); // длина слова

int count = 0; // счетчик количества вхождений

char *p = line; // указатель устанавливаем на начало строки

while( p = strstr(p, word)){

char *c = p; // указатель устанавливаем на начало слова

p += l_word; // указатель перемещаем на длину введенного

// слова

// слово не в начале строки?

if (c != line)

// символ перед словом не разделитель?

if(!ispunct(*(c-1)) && !isspace(*(c-1)) continue;

// символ после слова разделитель?

if(ispunct(*p) || isspace(*p) || (*p ==’\0’)) count++;

}

cout << “Кол-во вхождений слова”<< count << endl;

return 0;

}

В рассмотренном примере вводится служебная переменная с для хранения адреса начала вхождения подстроки. Символы, ограничивающие слово, проверяются с помощью функций, хранящихся в заголовочном файле <ctype.h>. Следующий за словом символ проверяется также на признак конца строки (нуль-символ).

Для многократного поиска вхождения подстроки в заголовке цикла используется функция strstr(). Очередной поиск должен выполняться с позиции, следующей за найденной на предыдущем шаге подстрокой. Для хранения этой позиции определяется вспомогательный указатель р, который на каждой итерации цикла наращивается на длину подстроки.

Давайте теперь рассмотрим другой вариант решения этой задачи. В С++ есть функция strtok(), которая разбивает переданную ей строку на лексемы (слова) в соответствии с заданным набором разделителей. Теперь нам не придется выделять и проверять начало и конец слова «вручную», а лишь сравнить с искомым словом слово, выделенное с помощью strtok(). Правда список разделителей придется задать вручную.

. . .

int main(){

const int len = 81;

char word[len], line[len];

cout<<”Введите строку не более 80 симв”;

cin.getline(line, len);

cout<<”Введите слово для поиска”; cin >> word;

char razdel[] = “,.!/”?<>)(|\*;:”; // ввод разделителей

int count = 0; // счетчик количества вхождений

char *token;

token = strtok(line, razdel); // адрес 1-го слова

while( token != NULL){

/* strtok заменяет на NULL разделитель, наход-ся после

найденного слова */

if(!strcmp(token, word)) count++; // сравнение слов

token = strtok(NULL, razdel); // поиск следующего слова

}

cout << “Кол-во вхождений слова”<< count << endl;

return 0;

}

Задание.

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

Использование специальных библиотечных функций и применение способа обработки строки как символьного массива обоснуйте.

5. Требования к отчету по домашней работе:

Отчет должен содержать:

1) распечатку или текст программы с комментариями;

2) результаты работы программы.

Варианты заданий.

1.Определить, сколько раз в сформированной вами строке встречается сочетание символов «а» и «в».

2. Определить, сколько раз в сформированной вами строке повторяются выбранные сим­волы, из которых состоит алфавит.

3. Определить, сколько раз в сформированной вами строке встречается каждый из симво­лов, и какое из заданных сочетаний этих символов наиболее часто встречаются.

4. Определить, сколько однотипных сочетаний (сочетания задаются интерактивно) содер­жится в списке фамилий студентов вашей группы.

5. Составить программу определения слов, содержащих цифровые символы.

6. Разработать программу кодирования и декодирования текста. Алфавит содержит 20 сим­волов.

7. В произвольно взятом предложении произвести замену слов, расположенных на чётных и нечётных позициях.

8. В произвольно сформированной строке, содержащей 60 латинских букв, требуется все вхождения lat заменить на tal.

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

10. В произвольно сформированной строке, заменить на РОВ первое вхождение С, если такое есть.

11. Выделить часть строки (первое вхождение) расположенной между двумя буквами. Бу­квы задаются в диалоге.

12. В произвольно взятом предложении отпечатать слова, у которых совпадают более двух букв.

13. В произвольно взятом предложении определить слово, содержащее максимальное коли­чество гласных букв.

14. В произвольно взятом предложении напечатать слова в обратном порядке.

15. В произвольно взятом предложении напечатать слова в алфавитном порядке.

16. Определить количество слов в заданном предложении, а также количество букв.

17. В произвольно взятом предложении удалить все лишние пробелы.

18. Вывести на печать первую и последнюю букву каждого слова.

19. В произвольно взятом предложении определить количество слов содержащих цифровые символы.

20. В произвольно взятом предложении определить количество слов заканчивающихся на мягкий знак.

21. В произвольно взятом предложении заменить слово « республика » на « государство ».

22. В произвольно взятом предложении определить слово, состоящее из максимального ко­личества символов.

23. В произвольно взятом предложении выделить слова заключённые в кавычки.

24. В произвольно взятом предложении (английский шрифт) заменить каждую первую букву слов, начинающихся с гласной буквы, на прописную.

25. В произвольно взятом предложении заменить каждую цифру 0…9 на слова «ноль», «один», …, «девять».

26. В произвольно взятом предложении вывести на экран все слова, начинающиеся на гласную букву.

27. В произвольно взятом предложении определить количество слов, состоящих из не более чем четырех символов.

28. В произвольно взятом предложении поменять каждые два слова местами и вывести его на экран.

29. В произвольно взятом предложении вывести на экран слова, содержащие заданное количество букв.

30. В произвольно взятом предложении подсчитать количество знаков препинания.

Контрольные вопросы.

1. В чем отличие символьной константы и строкового литерала?

2. Перечислите способы инициализации строки.

3. В чем состоит особенность работы со строками?

4. В чем состоит особенность ввода/вывода строк?

5. Как использовать указатель на строку?

Домашняя работа № 2.

Наши рекомендации