-1Информатика Практическое занятие 1-07 Практическое занятие 1-07 3.2.33. Ввод-вывод строк и символов 3.2.34. Функции обработки символов и строк 3.2.35. Проверка символов и преобразование символов 3.2.36. Определение длины строки 3.2.37. Копирование строк 3.2.38. Сравнение строк 3.2.39. Конкатенация строк 3.2.40. Поиск символов и строк в строке 3.2.41. Операции с блоками памяти 3.2.42. Преобразование строк в числа 3.2.33. Ввод-вывод строк и символов Ввод строк с клавиатуры или вывод строк на дисплей можно выполнить, используя описанные выше функции scanf() и printf(), а также спецификацию ввода-вывода %s. При работе со строками следует иметь в виду, что сначала нужно зарезервировать место в памяти для ее хранения, а затем использовать функцию ввода для загрузки строки. Так, использование следующих операторов для ввода строки char *name; scanf("%s", name); приведет к ошибке при выполнении программы (вид ошибки зависит от используемого компилятора). Ошибка вызвана тем, что объявленный указатель name в момент ввода не является адресом какой-либо строки. Простейшее решение этой проблемы – включить в объявление явное указание размера строки, как в приведенном примере: char *name[25]; printf("\nВведите фамилию: "); scanf("%s", name); printf("\nФамилия: %s", name); Если набрать на клавиатуре строку "Иванов", то на дисплей будет выведено: Фамилия: Иванов Другое возможное решение – использование динамической памяти будет рассмотрено позднее. При выделении памяти под строку необходимо принимать во внимание следующее. Если длина вводимой строки известна, то размер массива задается равным длине строки плюс 1 (для символа окончания строки '\0'). Если размер строки неизвестен или в один и тот же массив помещается несколько строк, то в качестве размера массива задается максимальная длина строки плюс 1 (так в приведенном примере предполагается, что любая вводимая фамилия не превышает 24 символа). Следует также иметь в виду, что все операции над строками, включая ввод и вывод, выполняются не над массивом символов, а размещенной в нем строкой, т.е. последовательностью символов в массиве, начиная с нулевой позиции, до позиции символа '\0' (но не включая ее). Если нужно ввести строку, содержащую пробел, например, фамилию и инициалы имени и отчества, то приведенная выше последовательность операторов введет только Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. -2Информатика Практическое занятие 1-07 фамилию. Так, если ввести с клавиатуры строку "Иванов И.И.", то на дисплей будет выведено то же, что и в приведенном примере, поскольку при использовании спецификации %s считается, что признаком окончания строки является пробел. В этом случае можно, конечно, ввести фамилию и инициалы как две строки, а затем их объединить, но проще воспользоваться функцией ввода строки char *gets(char *s); Эта функция, как и другие функции ввода-вывода строк и символов, рассматриваемые ниже, находится в разделе stdio.h стандартной библиотеки языка C. Функция gets() получает строку s из стандартного устройства ввода системы (обычно клавиатуры) и считывает символы до тех пор, пока не достигнет символа перевода строки '\n', который генерируется при нажатии клавиши Enter, либо символа конца файла (при вводе с клавиатуры в MS DOS и Windows этот символ генерируется при нажатии клавиш Ctrl+Z). Функция помещает в строку все символы, кроме последнего – '\n' или символа конца файла. Вместо него функция записывает в строку символ '\0', который, как указывалось ранее, служит в C признаком окончания строки. Если введен символ окончания файла или обнаружена ошибка, функция gets() возвращает значение NULL. Реализация ввода фамилии и инициалов с помощью функции gets() выглядит следующим образом: char *name[25]; printf("\nВведите фамилию и инициалы: "); gets(name); printf("\nФамилия: %s", name); Если ввести строку "Иванов И.И." и нажать клавишу Enter, то на дисплей будет выведено: Фамилия: Иванов И.И. Функция int puts(const char *s); выводит строку s на дисплей. В отличие от функции printf(), после окончания вывода строки функция puts() автоматически осуществляет переход на новую строку. Функция puts() прекращает вывод, встретив символ '\0', поэтому необходимо, чтобы строка содержала такой символ. Функция возвращает последний записанный символ '\n' или EOF (в случае ошибки). Так, вывод фамилии с помощью функции puts() можно выполнить следующим образом: printf("\nФамилия: "); puts(name); Строка вывода будет иметь тот же вид, что и в предыдущем примере, однако следующая функция вывода начнет вывод с новой строки. Иногда в качестве разделителя между данными необходимо использовать символразделитель, отличный от пробела или символа перевода строки (например, символ ';'). В этом случае строку лучше вводить посимвольно с анализом вводимого символа. Символы вводятся и последовательно записываются в строку до тех пор, пока не встретится символ-разделитель. Вместо него в строку в качестве последнего символа необходимо записать символ окончания строки – '\0'. Хотя символы можно вводить, используя функцию scanf() со спецификацией %c, лучше использовать функцию ввода символа: Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. -3Информатика Практическое занятие 1-07 int getchar(void) Эта функция не имеет параметров (часто параметр void опускается) и возвращает введенный символ в формате переменной типа int. Если введен символ конца файла или при вводе произошла ошибка, функция возвращает EOF. Пример использования функции getchar(): С клавиатуры вводятся фамилии и инициалы, отделенные друг от друга символом ';' или символом перевода на новую строку ('\n'). Максимальное количество фамилий – 20, максимальное количество символов в фамилии – 30. Ввод заканчивается при нажатии клавиш Ctrl+Z (символа конца файла – EOF), а затем введенные фамилии выводятся на дисплей: char symbol; /* Вводимый символ */ char name[20][30]; /* Массив строк фамилий и инициалов */ int i, j; /* Переменные цикла */ /* Признак окончания ввода (установлен в 0) */ int end_of_input = 0; int names_number; /* Количество введенных фамилий */ printf("\nВведите фамилии и инициалы, " "разделенные символом ';'" "\n(окончание ввода - нажатие клавиш Ctrl+Z):\n"); /* Ввод массива фамилий и инициалов */ for(i=0; i< 20; i++) { /* Ввод строки с фамилией и инициалами */ for(j=0; j< 30; j++) { /* Ввод символа */ symbol = getchar(); /* Если введен символ конца файла */ if(symbol == EOF) { /* Установка признака окончания ввода в 1 */ end_of_input = 1; break; /* Выход и цикла по j */ } /* Если введен символ-разделитель или символ перехода на новую строку */ if(symbol == ';' || symbol == '\n') break; /* Выход и цикла по j */ /* Запись введенного символа в текущую строку */ name[i][j] = symbol; } /* Запись символа окончания строки в текущую строку */ name[i][j] = '\0'; /* Если введен символ конца файла */ if(end_of_input == 1) break; /* Выход и цикла по i */ } /* Определение количества введенных фамилий */ Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. -4Информатика Практическое занятие 1-07 names_number = i + 1; /* Вывод введенных фамилий */ printf("\n\nВведены фамилии: "); for(i=0; i< names_number; i++) { printf("\n %d. %s", i+1, name[i]); } Пример вывода фрагмента программы: Введите фамилии и инициалы, разделенные символом ';' (окончание ввода - нажатие клавиш Ctrl+Z): Иванов И.И.;Петров П.П. Сидоров С.С. Введены фамилии: 1. Иванов И.И. 2. Петров П.П. 3. Сидоров С.С. Функция int putchar(int c) выводит символ – младший байт c на экран дисплея и, в качестве результата, возвращает посланный символ. Пример использования функции putchar(): Вывод заданной строки в обратном порядке: char string[] = "привет"; /* Исходная строка */ int i; /* Переменная цикла */ /* Вывод символов строки в обратном порядке */ printf("\nИсходная строка: %s" "\nСтрока в обратном порядке: ", string); for(i = 5; i >= 0; i--) { putchar(string[i]); } Вывод этого фрагмента программы будет иметь следующий вид: Исходная строка: привет Строка в обратном порядке: тевирп В разделе stdio.h стандартной библиотеки языка C определены также две функции для форматированного ввода-вывода в строки: int sprіntf(char *s, char *format[,аргумент-1[, аргумент-2,...]]) int sscanf(char *s, char *format[,аргумент-1[, аргумент-2,...]]) где строка s – идентификатор строки, в которую записываются (для sprintf()) или из которой считываются (для sscanf())данные. Аргумент format и аргумент-1, аргумент-2 и т.д. определяются так же, как и в функциях printf()и scanf(). Пример использования функций sscanf() и sprintf(): Если заданное поле вывода числа в функции printf() превышает выводимое число, то число дополнятся справа до заданной в спецификации ширины поля пробелами. Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. -5Информатика Практическое занятие 1-07 Иногда необходимо вместо пробела задать какой-либо другой символ для того, чтобы данное поле нельзя было изменить. В этом примере функция sprintf() используется для дополнительного форматирования выводимых данных (вместо пробела для цены товара выводится символ '*'). Функция sscanf() используется для ввода списков цен и наименований товаров, заданных как строки. /* Список, содержащий цены товаров */ char price_str[] = "12.60 123.00 4.90"; /* Список, содержащий наименования товаров */ char goods_str[] = "Калькулятор Часы Брелок"; /* Строка для преобразования формата числа */ char fprice[7]; /* Строка для вывода наименования товара */ char goods[15]; /* Строка формата для ввода цен товаров */ char fformat[3][11] = {"%f", "%*f %f", "%*f %*f %f"}; /* Строка формата для ввода наименований товаров */ char sformat[3][11] = {"%s", "%*s %s", "%*s %*s %s"}; float price; /* Цена товара */ int i, j; /* Переменные цикла */ /* Вывод заголовка */ printf("\nНаименование Цена" "\n----------------------"); for(i=0; i < 3; i++) { /* Ввод текущей цены товара из строки */ sscanf(price_str, fformat[i], &price); /* Вывод отформатированного числа в строку */ sprintf(fprice, "%6.2f", price); /* Изменение формата выводимого числа */ for(j=0; j < 6; j++) { /* Если символ в выводимом формате - пробел, */ if(fprice[j] == ' ') /* замена его на символ '*' */ fprice[j] = '*'; } /* Ввод текущего наименования товара из строки */ sscanf(goods_str, sformat[i], goods); /* Вывод текущей строки наименования и цены товара */ printf("\n%-13s %8s", goods, fprice); } Вывод этого фрагмента программы будет иметь следующий вид: Наименование Цена ---------------------Калькулятор *12.60 Часы 123.00 Брелок **4.90 Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. -6Информатика Практическое занятие 1-07 3.2.34. Функции обработки символов и строк символов В стандартной библиотеке C есть следующие группы функций, оперирующих с символами и строками символов: проверка символов и преобразование символов; операции над строками; операции над блоками памяти; преобразование строк в числа. 3.2.35. Проверка символов и преобразование символов Часто при анализе строк символов возникает задача определения типа символов, образующих строку. Например, правильным идентификатором языка C является строка, содержащая буквы, цифры и символ '_', причем цифра не должна быть первым символом идентификатора. Для проверки этого и других подобных условий можно использовать функции раздела ctype.h стандартной библиотеки языка C, приведенные в табл. 3.2.14. Табл. 3.2.14. Функции определения типа символов стандартной библиотеки C Имя функции Действие функции isalnum Проверка символов A-Z, a-z, 0-9. isalpha Проверка букв A-Z, a-z. iscntrl Проверка управляющих символов с кодами 0x00-0x1f и 0x7f. isdigit Проверка цифр 0-9. isgraph Проверка печатаемых символов, исключая пробел – 0x21-0x7e. islower Проверка букв a-z. isprint Проверка печатаемых символов – 0x20-0x7e. ispunct Проверка символов – знаков пунктуации. isspace Проверка символа пробела. isupper Проверка букв A-Z. isxdigit Проверка шестнадцатеричных цифр – 0-9, A-F. В приведенных функциях в качестве аргумента задаются значения типа int (проверяется только младший байт аргумента). Возвращаемое значение (типа int) равно 0, если условие проверки не выполняется и отлично от нуля в противном случае. Кроме приведенных в табл. 3.2.14 функций, в разделе ctype.h определены еще две функции: int tolower(int с); int toupper(int с); Функция tolower() проверяет, является ли младший байт аргумента c прописной буквой и, если является, буква переводится в нижний регистр. В противном случае функция возвращает значение аргумента. Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. -7Информатика Практическое занятие 1-07 Функция toupper() проверяет, является ли младший байт аргумента c строчной буквой и, если является, буква переводится в верхний регистр. В противном случае функция возвращает значение аргумента. Пример использования функции определения типа символа: Следующий фрагмент программы вводит строку и подсчитывает количество заглавных букв во введенной строке: char str[81]; /* Выделение памяти по строку */ char *ptr; /* Объявление указателя на строку */ /* Объявление счетчика заглавных букв, его инициализация */ int upper_case_count = 0; printf("\nВведите строку латинских букв: "); /* Ввод строки и присваивание указателю адреса первого символа строки */ ptr = gets(str); /* Просмотр символов строки до символа окончания строки */ while(*ptr != '\0') { /* Если символ – заглавная буква, */ if(isupper(*ptr)) /* Увеличение счетчика на 1 */ upper_case_count++; /* Присваивание указателю адреса следующего символа строки */ ptr++; } printf("\nКоличество заглавных букв в строке: %d", upper_case_count); Пример вывода фрагмента программы: Введите строку латинских букв: AbcDeg Количество заглавных букв в строке: 2 К сожалению, функции isalnum(), isalpha(), isgraph(), islower(), isprint() и isupper(), а также функции tolower() и toupper() не распознают буквы кириллицы. Более того, в зависимости от среды выполнения программы (MS DOS или Windows) для букв кириллицы используется либо кодировка CP866, либо кодировка Windows-1251. Поэтому необходимо программировать свои функции определения типа символов и преобразования символов. При программировании этих функций следует учитывать, что в кодировке Windows-1251 заглавные русские буквы А-Я имеют коды в диапазоне C0-DF, строчные буквы а-я – в диапазоне E0-FF. Буквы Ё(ё) имеют коды A8(B8), буквы Є(є) – коды AA(BA), буквы Ї(ї) – коды AF(BF), буквы Ґ(ґ) – коды A5(B4) и буквы І(і) – коды B2(B3). В кодировке CP866 заглавные русские буквы А-Я имеют коды в диапазоне 80-9F, строчные буквы а-п – в диапазоне A0-AF, а буквы р-я – в диапазоне E0-EF. Буквы Ё(ё) имеют коды F0(F1), буквы Є(є) – коды F2(F3), буквы Ї(ї) – коды F4(F5), а буквы І(і) кодируются так же, как и английские буквы I и i – кодами 49(69). Буквы Ґ(ґ) в кодировке CP866 отсутствуют. Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. -8Информатика Практическое занятие 1-07 Ниже приведен пример функции cwtoupper(), переводящей латинские, русские и украинские буквы в кодировке Windows-1251 в верхний регистр (для перевода букв из нижнего в верхний регистр используется рассмотренная в разд. 3.2.33 установка бита в заданной позиции в 0): int cwtoupper(int c) { /* Проверка и перевод латинских и русских букв */ if ((c >= 'a' && c <= 'z') || (c >= 'а' && c <= 'я')) c &= ~0x20; /* Проверка и перевод букв ё, є и ї */ if (c == 'ё'|| c == 'є' || c == 'ї') c &= ~0x10; if (c == 'і') /* Проверка и перевод буквы і */ c = 'І'; if (c == 'ґ') /* Проверка и перевод буквы ґ */ c = 'Ґ'; return c; /* Возврат символа */ } Эта функция использована в следующем фрагменте программы: char str[81]; /* Объявление вводимой строки */ int i; /* Переменная цикла */ /* Ввод строки */ printf("\nВведите строку: "); gets(str); /* Цикл преобразования символов строки */ for(i = 0; i < 21; i++) { /* Если текущий символ – конечный в строке */ if(str[i] == '\0') break; /* Выход из цикла */ /* Преобразование текущего символа строки */ str[i] = cwtoupper(str[i]); } /* Вывод преобразованной строки */ printf("\nСтрока в верхнем регистре: %s", str); Пример вывода этого фрагмента программы: Введите строку: Hello Привет Привіт Строка в верхнем регистре: HELLO ПРИВЕТ ПРИВІТ 3.2.36. Определение длины строки В программах довольно часто приходится выполнять обработку строк. Для работы со строками можно использовать функции раздела string.h стандартной библиотеки языка C. Эти функции могут пополнять следующие действия: определение длины строки; копирование строк; сравнение значений строк; слияние строк; поиск символов и строк в строке. Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. -9Информатика Практическое занятие 1-07 В разделе библиотеки определен также тип данных size_t (в BorlandC++ он определен как unsigned int) и пустой указатель NULL. Определение длины строки выполняется с помощью функции int strlen(const char *s); Функция возвращает количество символов в строке s, предшествующих символу '\0'. 3.2.37. Копирование строк Копирование одной строки в другую выполняется с помощью одной из следующих функций: char *strcpy(char *s1, char *s2); char *strncpy(char *s1, char *s2, size_t n); Первая функция копирует строку s2 (включая символ '\0') по адресу s1 и возвращает указатель на s1. Вторая функция копирует строку следующим образом: если длина строки s2 меньше n, то s2 копируется в s1 полностью и дополняется символами '\0' до размера n, иначе в s1 копируются первые n символов строки s2 и символ '\0' не записывается в конце строки s1. Пример использования функций strlen() и strcpy(): Модифицировать вводимую строку следующим образом: если какой-либо символ встречается в строке первый раз, он остается в модифицированной строке, иначе он удаляется из строки. char str[81]; /* Исходная строка */ char tmp[81]; /* Временная строка */ /* Счетчик количества символов во временной строке */ int tmp_count = 0; char symbol; /* Текущий символ исходной строки */ int i, j; /* Переменные цикла */ int is_found; /* Признак наличия символа в строке */ /* Ввод исходной строки */ printf("\nВведите исходную строку: "); gets(str); /* Формирование временной строки */ for (i = 0; i < strlen(str); i++) { /* Символ встречается первый раз в строке */ is_found = 0; /* Определение текущего символа строки */ symbol = str[i]; /* Просмотр временной строки */ for (j = 0; j < tmp_count; j++) { /* Если текущий символ уже есть во временной строке */ if (symbol == tmp[j]) { /* Установка признака наличия символа во временной строке в 1 */ is_found = 1; Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 10 Информатика Практическое занятие 1-07 break; /* Выход из цикла */ } } /* Если текущего символа еще нет во временной строке */ if(!is_found) { /* Добавление символа во временную строку */ tmp[tmp_count] = symbol; /* Увеличение счетчика количества символов во временной строке на 1 */ tmp_count++; } } /* Занесение символа окончания строки во временную строку */ tmp[tmp_count] ='\0'; /* Копирование временной строки в исходную строку */ strcpy(str, tmp); /* Вывод модифицированной строки */ printf("\nНовая строка: %s", str); Пример вывода фрагмента программы: Введите исходную строку: abcaaghhh Новая строка: abcgh 3.2.38. Сравнение строк Сравнение значений базовых переменных выполняется с помощью операций отношения, например сравнение на равенство переменных x1 и x2 выполняется с помощью операции x1 == x2. Идентификатор строки определяет не значение строки, как для базовой переменной, а указатель на начало строки. Поэтому операция str1 == str2 для строк str1 и str2 сравнивает не значения строк, а значения их указателей. Сравнение значений строк можно выполнить с помощью одной из следующих функций: int strcmp(const char *s1, const char *s2); int strncmp(const char *s1, const char *s2, size_t n); Первая функция посимвольно сравнивает строки s1 и s2. Вторая функция сравнивает строки посимвольно до тех пор, пока либо в одной из строк не встретиться символ '\0', либо не будут сравнены n символов строк. Функции возвращают значение 0, если все сравниваемые символы строк одинаковы. Если код первого несовпадающего символа строки s1 больше чем код соответствующего символа второй строки, то возвращается значение большее 0, иначе возвращается значение, меньшее 0. Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 11 Информатика Практическое занятие 1-07 Пример использования функции strcmp(): Фрагмент программы выполняет перевод вводимого с дисплея слова с русского языка на английский, а также обратный перевод (с английского языка на русский). Направление перевода определяется по первому символу введенного слова. /* Русский словарь */ char rus[5][15] = {"цветок", "дерево", "море", "птица", "трава"}; /* Английский словарь */ char eng[5][15] = {"flower", "tree", "sea", "bird", "grass"}; char word[15]; /* Вводимое слово */ int i; /* Переменная цикла */ int is_rus = 1; /* Введено русское слово */ /* Ввод исходной строки */ printf("\nВведите слово для перевода " "\n(русское или английское): "); gets(word); /* Проверка первого символа введенного слова */ if(isalpha(word[0])) /* Введено английское слово */ is_rus = 0; /* Просмотр словарей */ for (i = 0; i < 5; i++) { /* Если найдено русское слово */ if(is_rus && !strcmp(word, rus[i])) break; /* Выход из цикла */ /* Если найдено английское слово */ if(!is_rus && !strcmp(word, eng[i])) break; /* Выход из цикла */ } /* Если слово найдено */ if(i < 5) { /* Вывод перевода */ printf("\nПеревод: "); if(is_rus) puts(eng[i]); else puts(rus[i]); } /* Вывод сообщения */ else printf("\nСлово не найдено"); Примеры вывода этого фрагмента программы: 1. Введите слово для перевода (русское или английское): цветок Перевод: flower Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 12 Информатика Практическое занятие 1-07 2. Введите слово для перевода (русское или английское): tree Перевод: дерево 3. Введите слово для перевода (русское или английское): sun Слово не найдено 3.2.39. Конкатенация строк Слияние двух строк (или операция конкатенации строк) выполняется с помощью одной из следующих функций: char *strcat(char *s1, const char *s2); char *strncat(char *s1, const char *s2, size_t n); Первая функция объединяет строку s1 со строкой s2 и помещает результат в s1 (первый символ строки s2 записывается вместо символа строки s1). Вторая функция в случае, если n больше длины s2, действует также как и первая, в противном случае объединяются только первые n символов строки s2 и после окончания объединенной строки записывается символ '\0'. Обе функции возвращают указатель на строку s1. Пример использования функции strcat(): При сокращенной записи даты или времени, если значения дня месяц, номера месяца, часов, минут или секунд выражается одной цифрой, принято записывать эту цифру с нулем впереди, например: 03.05.2003. Форматный вывод с помощью функции printf() не обеспечивает такой возможности. Приведенная ниже функция out_pattern() преобразует вводимое число number в двухсимвольную строку field, в которой, если число содержит одну цифру, перед ним добавляется символ '0'. void out_pattern(int number, char field[]) { /* Строка для вывода числа */ char str_field[3]; /* Форматированный вывод числа в строку */ sprintf(str_field, "%d", number); /* Если длина поля числа равна 1 */ if(strlen(str_field) == 1) /* Добавление нуля перед числом */ strcat(field, str_field); else /* Копирование поля в строку */ strcpy(field, str_field); } Ниже приведен фрагмент программы, использующий функцию out_pattern() для вывода сокращенной записи даты рождения: struct date /* Структура для даты */ { int day; /* День месяца */ int month; /* Номер месяца */ Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 13 Информатика Практическое занятие 1-07 int year; /* Год */ } birth_date; /* Поле шаблона для дня */ char birth_day[3] ="0\0"; /* Поле шаблона для месяца */ char birth_month[3] ="0\0"; /* Ввод даты рождения */ printf("\nВведите дату Вашего рождения" "\n(число номер-месяца год): "); scanf("%d %d %d", &birth_date.day, &birth_date.month, &birth_date.year); /* Определение поля для вывода дня */ out_pattern(birth_date.day, birth_day); /* Определение поля для вывода месяца */ out_pattern(birth_date.month, birth_month); /* Вывод дня рождения */ printf("\nДень рождения: %s.%s.%d", birth_day, birth_month, birth_date.year); Пример вывода этого фрагмента программы: Введите дату Вашего рождения (число номер-месяца год): 3 5 1984 День рождения: 03.05.1984 3.2.40. Поиск символов и строк в строке Поиск заданного символа в строке выполняется с помощью функций: char *strchr(const char *s, int c); char *strrchr(const char *s, int c); Первая функция возвращает указатель на позицию первого появления (вхождения) символа c (преобразованного в тип unsigned char) в строке s, а вторая функция – указатель на последнее вхождение символа c в строке s. Если символ c не найден в строке, функции возвращают NULL. Пример использования функции strchr(): В приведенном ниже фрагменте определяются и выводятся на дисплей позиции всех вхождений символа 'И' в строку str: char str[] = "Иванов Иван Иванович"; /* Исходная строка */ char *ptr; /* Указатель на символ строки */ int str_length; /* Длина строки */ int i; /* Переменная цикла */ /* Установка указателя на начало исходной строки */ ptr = str; /* Определение длины строки */ str_length = strlen(str); for(i = 0; i < str_length; i++) /* Просмотр строки */ { /* Определение текущего вхождения символа 'И' в строку */ ptr = strchr(ptr, 'И'); Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 14 Информатика Практическое занятие 1-07 /* Если вхождение не найдено */ if(ptr == NULL) break; /* Выход из цикла */ /* Вывод номера и значения индекса текущего вхождения */ printf("\nИндекс %d = %d", i + 1, ptr - str); /* Установка указателя на следующий после найденного символ */ ptr++; /* Если достигнут конец строки */ if((ptr - str) >= str_length) break; /* Выход из цикла */ } Пример вывода этого фрагмента программы: Индекс 1 = 0 Индекс 2 = 7 Индекс 3 = 12 Поиск первого вхождения в строке символа из заданного набора символов выполняется с помощью функции char *strpbrk(const char *s1, const char *s2); Эта функция возвращает указатель на позицию первого вхождения любого из символов строки s2 в строке s1. Если в строке s1 не найдено ни одного символа строки s2, функция возвращает NULL. Если в предыдущем примере заменить оператор ptr = strchr(ptr, 'И'); оператором ptr = strpbrk(ptr, "Ии"); то фрагмент программы выведет все вхождения буквы и в строке str, независимо от регистра этой буквы: Индекс 1 = 0 Индекс 2 = 7 Индекс 3 = 12 Индекс 4 = 18 Поиск первого вхождения в строке заданной подстроки выполняется с помощью функции char *strstr(const char *s1, const char *s2); Эта функция возвращает указатель на позицию первого вхождения подстроки s2 в строке s1. Если подстрока s2 не найдена в строке s1, функция возвращает NULL. Если в предыдущем примере заменить оператор ptr = strchr(ptr, 'И'); оператором ptr = strstr(ptr, "Иванов"); то фрагмент программы выведет все вхождения подстроки "Иванов" в строке str: Индекс 1 = 0 Индекс 2 = 12 Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 15 Информатика Практическое занятие 1-07 Функция size_t strspn(const char *s1, const char *s2); возвращает длину начальной подстроки в строке s1, которая содержит все символы строки s2, а функция size_t strcspn(const char *s1, const char *s2); возвращает длину начальной подстроки в строке s1, которая не содержит ни одного символа строки s2. Так, если определить следующую строку: char str[] = "Иванов Иван Иванович"; то вызов функции strspn(str, "Иванов ") возвратит целое число 18 (длину строки "Иванов Иван Иванов", в которой встречаются все символы из строки "Иванов "), а вызов функции strcspn(str, "ортуф") возвратит целое число 4 (длину строки "Иван", в которой нет ни одного символа из строки "ортуф"). При работе со строками довольно часто встречается задача анализа строки для случая, когда строка состоит из последовательностей символов, называемых словами (words) или лексемами (tokens), которые отделены друг от друга одним или несколькими символами, называемыми символами-разделителями. Примером такой строки является строка, содержащая предложение или предложения текста, в которой лексемами являются слова, а символами-разделителями – знаки препинания. Другим примерам является программа на языке C, в которой лексемами являются идентификаторы языка, а символами-разделителями является пробел и символы, приведенные в табл. 3.2.1. Функция char *strtok(const char *s1, const char *s2); выполняет поиск лексем в строке s1. Разделителями между словами является любой из символов, заданных в строке s2 (слова в s1 могут отделять друг от друга несколько символов-разделителей). Лексемы могут содержать любые символы, за исключением символов, заданных в строке s2. При первом вызове функции strtok() для заданных значений параметра s1 и строки s2 возвращается указатель на начало первой лексемы в строке s1. Для определения второй и следующих лексем в качестве первого аргумента функции strtok() передается NULL. Если в строке s1 больше не осталось лексем, функция возвращает NULL. Пример использования функции strtok(): char istr [100]; /* Объявление исходной строки */ char fword [100]; /* Объявление строки поиска */ char *sword; /* Объявление указателя на строку */ /* Строка разделителей */ char divstr [] = " .,;:?!()[]{}<>\"\'-+-*/"; /* Счетчики количества слов и найденных слов */ int nw, nfw = 0; /* Ввод исходной строки */ printf ("\nВведите исходную строку: "); gets(istr); Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 16 Информатика Практическое занятие 1-07 /* Ввод слова поиска */ for (;;) { printf ("\nВведите слово поиска: "); gets(fword); /* Проверка слова на символы-разделители */ if (strpbrk(fword, divstr) == NULL) break; /* Выход из цикла */ else /* Повторный ввод строки */ printf("Неверное слово."); } sword = strtok (istr, divstr); /* Поиск первого слова */ /* Просмотр слов строки */ for (nw = 0 ; sword != NULL ; nw++) { /* Определение количества найденных слов в строке */ if (strcmp (fword, sword) == 0) nfw++; /* Поиск следующего слова */ sword=strtok (NULL, divstr); } /* Вывод результата */ printf ("\nКоличество слов в строке: %d. " "Количество слов <%s>: %d.", nw, fword, nfw); Пример вывода этого фрагмента программы: Введите исходную строку: Иванов Иван Иванович; Сидоров Иван Петрович Введите слово поиска: Иван Количество слов в строке: 6. Количество слов <Иван>: 2. 3.2.41. Операции с блоками памяти Помимо функций работы со строками, в разделе string.h определены также функции работы с непрерывными блоками памяти. Такими блоками является не только строки, но и массивы вообще (например, массивы целых чисел типа int), а также структуры и объединения. Поэтому в качестве типа для указателей на блоки памяти в функциях используется обобщенный тип указателя (void *), который может быть присвоен указателю на любой тип данных. Для копирования заданного количества байт из одного блока памяти в другой используются функции void *memcpy(void *s1, const void *s2, size_t n); void *memmove(void *s1, const void *s2, size_t n); Функции копируют первые n байт блока памяти s2 в блок памяти s1 и возвращают указатель на блок памяти s1. Различие между выполнением функций memcpy() и memmove() заключается в следующем. Результат выполнения функции memcpy() не определен, если два блока памяти перекрывают друг друга. В этом случае функция memmove() работает правильно, поскольку она сначала копирует n байт из Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 17 Информатика Практическое занятие 1-07 блока s2 во временный блок памяти, и уже потом копирует содержимое временного блока памяти в s1. Пример использования функций memmove() и memcpy(): Следующий фрагмент программы добавляет заданную подстроку в начало вводимой строки: char str[41]; /* Исходная строка */ char add[11]; /* Вставляемая строка */ int str_length; /* Длина исходной строки */ int add_length; /* Длина вставляемой подстроки */ /* Ввод исходной строки и определение ее длины */ printf("\nВведите исходную строку " "\n(не более 30 символов): "); gets(str); str_length = strlen(str); /* Ввод добавляемой подстроки, определение ее длины */ printf("\nВведите вставляемую в начало строки подстроку " "\n(не более 10 символов): "); gets(add); add_length = strlen(add); /* Смещение исходной строки вправо - можно использовать только функцию memmove() */ memmove(&str[add_length], str, str_length +1); /* Копирование добавляемой подстроки в начало строки (без символа '\0') - можно использовать и memcpy() и strncpy() */ memcpy(str, add, add_length); /* Вывод измененной строки */ printf("\nИзмененная строка: %s", str); Пример вывода этого фрагмента программы: Введите исходную строку (не более 30 символов): Иван Иванович Введите вставляемую в начало строки подстроку (не более 10 символов): Иванов Измененная строка: Иванов Иван Иванович Функция int memcmp(const void *s1, const void *s1, size_t n); сравнивает первые n байт блока памяти s1 с первыми n байт блока памяти s2 и возвращает значение 0, если все сравниваемые байты одинаковы. Если код первого несовпадающего байта строки s1 больше чем код соответствующего байта второй строки, то функция возвращает значение большее 0, иначе – значение, меньшее 0. Как видно из описания, действие функции memcmp() аналогично действию функции strncmp(), однако эту функцию можно использовать не только для строк, но и для других типов данных. Пример использования функции memcmp(): Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 18 Информатика Практическое занятие 1-07 Проверка симметричности массива (у симметричного массива значение первого элемента равно значению последнего элемента, значение второго элемента – значению предпоследнего элемента и т.д.) int a[7] = {0, 1, 2, 3, 2, 1, 0}; /* Исходный массив */ int is_symmetric = 1; /* Признак симметричности массива */ int i, j; /* Переменные цикла */ /* Цикл по элементам массива */ for(i =0, j = 6; i < j; i++, j--) { /* Если симметричные элементы массива не равны */ if(memcmp(&a[i], &a[j], 2)) { /* Массив не симметричный */ is_symmetric = 0; break; /* Выход из цикла */ } } /* Вывод сообщения */ if(is_symmetric) printf("\nМассив a симметричный"); else printf("\nМассив a несимметричный"); Выводом этого фрагмента программы будет сообщение: Массив a симметричный Функция void *memchr(const void *s, int c, size_t n); возвращает указатель на позицию первого вхождения символа c (преобразованного в тип unsigned char) в первых n байтах блока памяти s. Если символ c не найден, функция возвращает NULL. Функция memchr() действует аналогично функции strchr(), за исключением того, что можно задать поиск не во всей строке, а в начальном фрагменте строки. Использование в качестве шаблона поиска только байта ограничивает возможности применения этой функции для типов данных, отличных от строк. Так ее нельзя использовать для поиска позиции первого элемента с заданным значением в массиве типа int, поскольку значение элемента массива этого типа имеет длину 2 байта. Функция void *memset(const void *s, int c, size_t n); копирует символ c (преобразованный в тип unsigned char) в первые n байт блока памяти s, например, для строки char divider[41]; выполнение операторов: memset(divider, '*', 40); divider[40] = '\0'; приведет к заполнению строки символами '*'. Для функции memset() действуют те же ограничения, что и для функции memchr(), поэтому ее нельзя использовать, например, для присваивания всем элементам массива типа int одинаковых значений. Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 19 Информатика Практическое занятие 1-07 Помимо рассмотренных выше функций работы со строками среды программирования на языке C/C++ предлагают свои, дополнительные функции работы со строками. Например, функция stricmp() в BorlandC/C++ позволяет сравнивать строки без учета регистра символов, а функции strupr() и strlwr() переводят строку соответственно в верхний и нижний регистры. 3.2.42. Преобразование строк в числа К функциям работы со строками относятся также функции преобразования строк в числа разных типов, объявленные в разделе stdlib.h. стандартной библиотеки языка C (обратное преобразование числа в строку можно выполнить с помощью функции sprintf()). Функции int atoi(const char *nptr); long atol(const char *nptr); double atof(const char *nptr); преобразуют строку соответственно в число типа int, long или double. Преобразование выполняется до первого символа, не являющегося частью числа (например, буквы). Перед числом может быть задано любое количество пробелов. Функции возвращают значение 0 соответствующего типа, если строка не может быть преобразована в число (например, содержит только буквы). Так строка "3.52 грн" в результате вызова функций atoi(), atol() и atof() будет преобразована соответственно в число 3 типа int, число 3 типа long и число 3.52 типа double, а строка "$200" будет преобразована этими тремя функциями в число 0 соответствующего типа, поскольку первым символом строки является нецифровой символ '$'. Пример использования функции atof(): При вводе-выводе чисел с помощью функций форматного ввода-вывода разделителем между целой и дробной частью числа является точка. Однако для некоторых пользователей более привычным является использование в качестве разделителя запятой. Для ввода и вывода числа с плавающей запятой можно использовать приведенные ниже функции get_number() и put_number(). Функция ввода числа с плавающей запятой: double get_number(void) { /* Строка для вводимого числа */ char str_field[15]; char *point_ptr; /* Указатель на символ */ scanf("%s", str_field); /* Ввод числа в строку */ /* Определение индекса запятой */ point_ptr = strchr(str_field, ','); if(point_ptr != NULL) /* Если запятая в числе есть, */ *point_ptr ='.'; /* замена запятой на точку */ /* Преобразование строки в число типа double и возврат числа */ return atof(str_field); } Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 20 Информатика Практическое занятие 1-07 Функция вывода числа с плавающей запятой (параметрами являются выводимое число и спецификация вывода для этого числа): void put_number(double number, char format[]) { char str_field[15]; /* Строка для выводимого числа */ char *point_ptr; /* Указатель на символ */ /* Ввод числа в строку */ sprintf(str_field, format, number); /* Определение индекса точки */ point_ptr = strchr(str_field, '.'); if(point_ptr != NULL) /* Если точка в числе есть, */ *point_ptr =','; /* замена точки на запятую */ printf("%s", str_field); /* Вывод числа */ } В приведенном ниже фрагменте программы вводятся два числа в формате с плавающей запятой, а затем подсчитывается и выводится их сумма также в формате с плавающей запятой: double x, y, z; printf("\nВведите x: "); x = get_number(); printf("\nВведите y: "); y = get_number(); z = x + y; printf("\nСумма: "); put_number(z, "%g"); Пример вывода этого фрагмента: Введите x: 4,5 Введите y: 8,9 Сумма: 13,4 Функции long strtol(const char *npt, char **, int base); unsigned long strtoul(const char *npt, char **ept, int base); long strtod(const char *npt, char **ept); преобразуют строку npt соответственно в число типа long, unsigned long или double. Преобразование так же, как и в функциях atol() и atof(), выполняется до первого символа, не являющегося частью числа, но в этих функциях в аргументе ept возвращается адрес первого символа, не являющегося частью числа. Если при вызове функции задать для ept значение NULL, то значение ept функцией не возвращается. Аргумент base в функциях strtol() и strtoul() задает систему счисления, в которой задано число в строке (в диапазоне от 2 до 36). Если для base задано значение 0, то система счисления определяется по первым символам преобразуемого числа (если первый символ – '0', то считается, что число представлено в восьмеричной системе счисления, если первые два символа – '0X' или '0x', то считается, что число представлено в шестнадцатеричной системе счисления, иначе считается, что число представлено в десятичной системе счисления). Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 21 Информатика Практическое занятие 1-07 Пример использования функции strtol(): В этом примере вводимая строка содержит список целых чисел, отделенных друг от друга запятой. При просмотре строки каждый элемент списка преобразуется в число типа int и помещается в массив a. char str[81]; /* Исходная строка */ int a[10]; /* Массив целых чисел */ /* Указатели на начало и окончание числа в строке*/ char *ptr, *endptr; int i; /* Переменная цикла */ /* Ввод строки с числами */ printf("\nВведите строку целых чисел, разделенных" " запятыми\n(не более десяти чисел): "); gets(str); ptr = str; /* Установка указателя на начало строки */ for(i = 0; i <= 10; i++) /* Запись чисел в массив */ { /* Получение текущего элемента массива */ a[i] = (int) strtol(ptr, &endptr, 0); /* Если достигнут конец строки */ if(endptr > str + strlen(str)) break; /* Выход из цикла */ ptr = endptr + 1; /* Вывод текущего элемента массива */ printf("\na[%d]=%d", i, a[i]); } Пример вывода этого фрагмента программы: Введите строку целых чисел, разделенных запятыми (не более десяти чисел): 4,12,42,8 a[0]=4 a[1]=12 a[2]=42 a[3]=8 Файл: 681451317 Создан: 22.09.2003 Модифицирован: 29.04.2016 Автор: Шонин В.А.