Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
файлы и строки.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
204.8 Кб
Скачать

Функции работы со строками и символами

Строка представляет собой массив символов, заканчивающийся нуль-символом’\0’. В С++ есть две возможности работы со строками: функции, унаследованные из библиотеки С (заголовочный файл <string.h> или <cstring>), и библиотечный класс С++ string, предоставляющий более широкие возможности представления, обработки и контроля строк. Класс string более безопасен, чем С-строки, но и более ресурсоемок. Для его грамотного использования требуется знание основ объекто-ориентированого программирования, поэтому мы рассмотрим его позже, а сейчас ограничимся строками С.

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

Память под строки, как и под другие массивы, может выделятся как компилятором, так и непосредственно в программе(динамически). Длина динамической строки может задаваться выражением, длина не динамической строки только - константным выражением. Чаще всего используют частный случай константного выражения – именованную константу.

const int len_str=80;

char str[len_str];

при задании длины необходимо учитывать завершающий нуль-символ. Например,в приведенной выше строке можно хранить не 80 символов, а только 79. При описании можно инициализировать строку строковой константной(нуль-символ, следующий за последним символом, формируется автоматически):

char a[100]=”Пример стороки”;

Если строка инициализируется, её размерность можно опускать – компилятор сам выделит память, достаточную для размещения всех символов строки и завершающего символа:

char a[]=”Пример стороки”;

Для размешения строки в динамической памяти надо описать указатель на char, а затем выделить память с помощью new или malloc:

char *p = new char[m];

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

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

char *str =”Пример константной строки”;

Присваивание вида str[2]='w' для такого указателя, хотя и компилируются, могут вызывать ошибки времени выполнения.

Для ввода вывода используются известные функции scanf и printf, задав спецификацию формата %s:

char s[10];

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

Имя строки, как и имя любого массива, является указателем на его начало, поэтому операция взятия адреса(&) опущена. Ввод будет выполнятся до первого пробельного символа(пробела, знака табуляции или символа перевода строки): если ввести несколько слов, воспринимается только первое. Чтобы ввести строку, состоящую из нескольких слов, используется спецификация %c(символы) с указанием максимального количества вводимых символов, например scanf(“%10c”,s).(ожидает все 10 символов)

Количество символов может быть только целой константой. При выводе можно задать перед спецификацией %s количество позиций отводимых под строку: printf(“%15s”,s). строка при этом выравнивается по правому краю отведенного поля. Если заданного количества недостаточно для размешения строки, оно игнорируется, и строка выводится целиком.

Библиотека содержит функции gets и puts, специально предназначенные для ввода-вывода строк.

gets(s); puts(s);

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

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

Функциями семейства printf удобнее пользоваться в том случае, если в одном операторе требуется ввести или вывести данные различных типов. Если же работа выполняется только со строками, проще применять специальные функции для ввода вывод строк gets и puts.

Cледующая программа присваивает буквы от А до Z переменной alphabet, используя цикл for. Затем программа добавляет символ NULL в эту переменную и выводит ее с помощью cout.

char alphabet [27]; // 26 буквы плюс NULL char letter;

int index=0;

for (char letter = 'A'; letter <= 'Z'; letter++, index++)

alphabet[index] = letter;

alphabet[index] = '\0';

printf( "Bukva %s", alphabet);

При рассмотрении программ на C++ вы можете встретить символы, заключенные в одинарные кавычки (например, 'А') и символы, заключенные в двойные кавычки ("А"). Символ внутри одинарных кавычек представляет собой символьную константу. Компилятор C++ выделяет только один байт памяти для хранения символьной константы. Однако символ в двойных кавычках представляет собой строковую константу — указанный символ и символ NULL (добавляемый компилятором). Таким образом, компилятор будет выделять два байта для символьной строки.

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

int len_string(char string[]){

int i;

for(i=0;string[i]!=0;i++);

return i;

}

void main(void){

cout<<len_string("Привет, C++!");

cout<<len_string("Учусь программировать на C++");

}

Как видите, функция len_string трактует параметр символьной строки как массив:

int len_string(char string[])

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

Операции со строками.

Для строк не определены операции присваивания, сравнения, сцепления поскольку они не являются основными типами данных.Эти операции делаются посимвольно «вручную» или с помощью функции библиотеки.

Библиотека С содержит функции копирования строк (strcpy, strncpy), сравнения (strcmp, strncmp), объединения строк (strcat, strncat), поиска подстроки (strstr), поиска вхождения символа (strchr, strrchr, strpbrk), определения длины строки (strlen) и др.

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

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

str1[0]=’H’;

str1[1]=’e’;

str1[2]=’l’;

str1[3]=’l’;

str1[4]=’o’;

str1[5]=’\0’;

Однако, это совершенно неудобно. Не зная о правильных способах, начинающие программисты часто «выдумывают» свои способы присваивания строк, конечно, неправильные. Приведу несколько примеров:

char str1[10], str2[10];

str1="Hello";

str2=str1;

//Одна и та же ошибка в обоих операторах =.

//Имя массива нельзя использовать в левой части оператора присваивания.

Эта ошибка относительно безопасна, так как приводит к сбою на этапе компиляции. Есть и гораздо более опасная ошибка.

char str1[10]= "Hello";

char* str2;

str2=str1;

str2[1]='u';

Этот код откомпилируется, но, возможно, содержит «идеологическую» ошибку. Неправильно полагать, что в str2 теперь содержится копия str1. На самом деле этот указатель указывает не на копию, а на ту же самую строку. При любом изменении содержимого str2 изменяется str1. Однако, если именно это и требуется, то все в порядке.

Еще один вариант присваивания указателей – присваивание их строковым литералам. Как вы помните, тип строкового литерала – const char*, а значит такой код работает:

const char* str;

str="Hello";

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

char* str; // Нет const

str="Hello";

Здесь мы имеем дело с наследством C, в котором отсутствовал const. Поэтому стандарт С++ разрешает такое присваивание. Что может иметь неприятные последствия:

char* str; // Нет const

str="Hello";

str[1]=’u’;

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

const char* str; // const есть

str="Hello";

str[1]=’u’; //error: l-value specifies const object

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

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

char* strcpy(char* dest, const char* src)

Функция посимвольно копирует содержимое строки, на которую указывает src в строку, на которую указывает dest и возвращает dest. Так как массив может быть преобразован в указатель, такой вызов функции абсолютно легален:

char str1[10], str2[10];

strcpy(str1, "Hello");

strcpy(str2, str1);

При использовании этой функции следует соблюдать осторожность. Опасность заключается в том, что даже если исходная строка окажется больше, чем память, выделенная для второй строки (dest) (программистом через malloc или компилятором при использовании массивов), функция strcpy никак про это узнать не сможет и продолжит копирование в невыделенную память. Разумеется, последствия будут катастрофическими.

Снизить риск такого развития событий способна функция

char* strncpy(char* dest, const char* src, size_t count)

Последний параметр – максимальное количество копируемых символов. Таким образом, передавая туда размер приемника, вы гарантируете, что функция никогда не выйдет за пределы выделенной памяти. Однако помните, что если исходная строка будет скопирована не полностью, нуль-символ не появится в результирующей строке. Его придется записать самостоятельно.