МИНИСТЕРСТВО ПО НАУКЕ И ОБРАЗОВАНИЮ РФ ВОЛГОГРАДСКОГО ГОСУДАРСТВЕННОГО ТЕХНИЧЕСКОГО УНИВЕР-

advertisement
МИНИСТЕРСТВО ПО НАУКЕ И ОБРАЗОВАНИЮ РФ
ВОЛЖСКИЙ ПОЛИТЕХНИЧЕСКИЙ ИНСТИТУТ (филиал) ГОУ ВПО
ВОЛГОГРАДСКОГО ГОСУДАРСТВЕННОГО ТЕХНИЧЕСКОГО УНИВЕРСИТЕТА
КАФЕДРА «ИНФОРМАТИКА И ТЕХНОЛОГИЯ ПРОГРАММИРОВАНИЯ»
Работа со строками в языке Си
Методические указания к лабораторным работам по
курсу «Основы программирования»
Волгоград 2011
УДК 004.056
Рецензент: к.т.н., доцент каф. ВАЭ и ВТ ВПИ (филиал) ВолгГТУ Капля В.И.
МЕТОДИЧЕСКИЕ УКАЗАНИЯ К ЛАБОРАТОРНЫМ РАБОТАМ: Работа со строками в
языке Си/ Сост. Лясин Д.Н., Фадеева М.В..; Волгоград. гос. техн. ун-т. - Волгоград, 2011, –
21 с.
Содержатся сведения, необходимые для изучения основ обработки
строковых данных на языке Си; рассмотрены способы представления строковых данных в программе, основные принципы обработки строковых данных;
дан обзор функций обработки строк на языке Си; приведены примеры решения типовых задач. Приведены варианты заданий к лабораторным работам.
Предназначены для студентов, обучающихся по направлениям
230100 "Информатика и вычислительная техника" и 231000 "Программная
инженерия" всех форм обучения в рамках курса «Основы программирования».
Ил. 2.
Библиогр.: - 6 назв.
Издается по решению редакционно-издательского совета Волгоградского государственного технического университета
©
Волгоградский государственный
технический университет, 2011
©
Волжский политехнический институт, 2011
Лабораторная работа №5
Работа со строками в языке Си
Цель: изучить способы хранения строковой информации в программах на
языке Си, получить навыки обработки строковой информации.
1. Основные сведения
Символьная информация очень часто является предметом обработки в
программах на языках высокого уровня. Примером могут служить как простой вывод текстовой информации на экран для информирования пользователя о ходе вычислительного процесса или результатах его работы, так,
например, обработка информации из текстового файла, требующая выполнения специфических операций слияния строк, удаления подстрок из текста,
изменения порядка следования слов или фраз и т.п. Для решения задач подобного класса в языке Си поддерживается работа со строками – объектами,
хранящими символьную информацию.
Фактически, строки рассматриваются как массивы, элементами которых являются объекты символьного типа (char или unsigned char). Однако,
смысловая связь элементов строки, объединяющая буквы в слова, а слова в
предложения, обособляет строки от массивов других типов, поэтому для них
предусмотрены как специальные способы обозначения, так и алгоритмы обработки в виде специализированных функций.
Строковые константы в языках Си и Си++ помещаются в кавычки:
“Это строка на языке Си”
Этот объект имеет в программе тип char * и указывает на то место в памяти программы, где расположена данная строка. При этом размер выделенного под строку участка памяти (при условии кодировки информации в стандарте ANSI) равен количеству символов строки и еще один символ (терминатор строки '\0').
Символьные константы заключаются в апострофы:
‘A’ – символ А, ' ' – символ пробел.
Эти объекты имеют тип char и их значение – код соответствующего
символа в кодировке ASCII. Если известен ASCII-код символа, то его можно
представить в программе в виде '\код8' или ‘\xкод16', где код8 – код символа
в восьмеричной системе счисления, а код16 – в шестнадцатеричной. Например, следующий код:
cout<<“\x48\x45\x4c\x4c\x4f”;
выведет на экран строку HELLO, поскольку ‘\x48’-код символа ‘H’, ‘\x45’ –
символа ‘E’ и т.д.
Для кодирования управляющих символов в языке Си используются
также специализированные Esc-последовательности:'\n' –перевод строки
3
‘\t' – горизонтальная табуляция
‘\r' –возврат курсора к началу
строки
'\\' – обратный слеш
'\'' – апостроф
'\”’ – кавычка
'\a' – сигнал-звонок
‘\b’ – возврат на одну позицию
'\f' – перевод страницы
‘\v’ – вертикальная табуляция
Если строковые данные изменяются в процессе выполнения программы, то их необходимо хранить в переменных. Строковая переменная
определяется в языке Си как массив:
char str[20];
Как всякую переменную, строку можно инициализировать при определении с помощью константной строки:
char str[20]=”Привет”;
Инициализацию можно осуществлять и поэлементно, но такая форма
записи неудобна:
char str[20]={'П’, ’р’, ‘и’, ‘в’, ‘е’ ‘т’, ‘\0’};
Обращает на себя внимание необходимость в таком случае обязательно
указывать терминатор ‘\0’ (символ, определящий конец полезной информации в строке) в конце строки. В константных строках терминатор добавляется в конец строки автоматически.
После такого определения можно работать с каждым символом строки
как с обычным элементом массива:
str[0]=’п’;
//теперь строка начинается со строчной буквы
cout<<str[2]; //выводим на экран букву и
Несмотря на схожесть строк с определением обычных массивов, они
имеют ряд уникальных свойств. Так, например, ввод-вывод элементов целочисленного массива всегда осуществляется поэлементно:
int mas[20];
for (i=0; i<20; i++)
cin >> mas[i];
//вводим очередной элемент массива
Однако, поэлементный (побуквенный) ввод текстовой информации, когда каждый символ необходимо подтверждать клавишей Enter был бы
неудобен. В этой связи функции ввода вывода языков Си и Си++ поддерживают работу со строками, позволяя вводить или выводить содержимое строки
целиком:
Ввод строки с клавиатуры
Вывод содержимого строки на экран
cout << str; //или printf(”%s”, str);
char str[20];
cin >> str;//или scanf(”%s”, str)
Необходимо сразу отметить, что ввод текстовой информации с использованием функции scanf или объекта cin не всегда допустим, поскольку эти объ4
екты считают пробел за разделитель вводимых объектов и поэтому позволяют вводить строку до первого пробела. Если необходим ввод данных, содержащих пробелы, лучше воспользоваться специальной функцией gets:
char *gets(char *s);
В отличие от стандартных средств ввода данных, gets допускает ввод
пробельных символов (пробел, символ табуляции).
char str[20];
gets( str); // можно вводить строку с пробелом
Использование функции gets небезопасно: пользователь может ввести
символов больше, чем зарезервировано под строку, и это приведет к порче
областей данных, смежных с занимаемой строкой. Безопасной версией функции является gets_s:
char *gets(char *s, size_t sizeInCharacters);
где параметр sizeInCharacters задает максимально возможное количество
считываемых из входного потока символов:
char str[20];
gets_s( str, 20); //безопасно от переполнения буфера
На практике можно использовать также функцию fgets в форме:
fgets(str, sizeof(str), stdin); /* считать из входного потока
stdin(c клавиатуры) не более sizeof(str) символов в строку str */
Вывод содержимого строки с использованием стандартных средств
(cout, printf) не вызывает проблем, но существует и специализированная
функция для вывода строк puts:
int puts(const char *s);
char str[20];
…
puts( str ); //выводим содержимое строки на экран
Важную роль в обработке данных в строке играет так называемый
терминатор - символ с нулевым кодом, завершающий информационную
часть строковой переменной. Рассмотрим фрагмент программы:
char str[20]=”Hello”, str1[30];
puts( str );
gets_s(str1, 30);
fputs(str1);
Что будет выведено на экран в первом и втором случае? Все 20 символов строки str или только строка “Hello”, занимающая ее первые несколько
символов? Во втором случае на экран выведется 30 символов строки str1 или
только та строка, которую перед этим введет пользователь? Очевидно, что на
экран должна выводиться лишь полезная, информационная часть строки, а ее
хвост, заполненный компьютерным «мусором», должен игнорироваться при
выводе и выполнении других функций обработки строки.
5
Для отделения информационной строки от неинициализированной ее
части используется терминатор '\0'. Все функции для работы со строками
языка Си учитывают наличие терминатора. Когда строка вводится с клавиатуры, после всех введенных символов в строку добавляется терминатор. Запись константной строки ”…” предполагает наличие терминатора на месте
закрывающейся кавычки. Функции обработки строк (объединения, поиска,
сравнения и т.д.) просматривают содержимое строки до терминатора. Поэтому непредсказуемо закончится действие такого фрагмента:
char str[6]=”Hello”; //5 символов строки+ терминатор
str[5]='!'; //изменяем символ с индексом 5 – это был терминатор
fputs(str); /* строка выводится, пока не встретится терминатор, но мы его удалили из строки */
Приведенный пример будет выводить информацию, начиная с адреса
начала строки в памяти, до тех пока не встретит байт с нулевым значением.
Данный пример подчеркивает необходимость осторожного использования
операций посимвольного изменения данных в строке.
Для обработки текстовой информации типичными являются операции
вставки и замены подстрок, их поиска и удаления, слияния и сравнения
строк. Для реализации этих операций удобно использовать специализированные функции из библиотек string.h, stdlib.h, stdio.h. Например, для получения длины строки (ее информационной части до терминатора) используется функция strlen библиотеки string.h:
int strlen(char * string);
Рассмотрим пример ее работы и отличие от операции взятия размера
sizeof
char str[20]=”Hello”;
cout<<strlen(str); //выведет 5
cout<<sizeof(str); //выведет 20
Безусловно, можно получить размер строки, не используя эту функцию:
char str[20]=”Hello”;
int Len=0;
for ( ; str[ Len ]!=0 ; Len++); //можно короче:for(; str[ Len ]; Len++);
cout<<Len;
В приведенном примере осуществляется подсчет элементов строки от
ее начала до символа с нулевым значением (кодом). Но использование специализированных функций для обработки строк удобнее и эффективнее с
точки зрения размера результирующего кода.
Рассмотрим задачу копирования одной строки в другую. Простое копирование с использованием операции присваивания недопустимо:
char str[20]=”Hello”, str1[20];
str1=str; //Ошибка
6
Такое присваивание подразумевает изменение адреса строки str1, но
он является константным указателем. Можно опять предложить свой алгоритм:
char str[100]=“One, two, three”, temp[50];
for (i=0;str[i]!=0;i++)
temp[i]=str[i]; //посимвольно копируем str в temp
temp[i]=0; //завершим строку терминатором
Гораздо удобнее воспользоваться стандартной функцией копирования
строк strcpy:
char *strcpy(char *dest, const char *src);
Эта функция копирует содержимое строки src в строку dest, как результат возвращает адрес строки приемника dest.
char str[100]=“One, two, three”, temp[50];
strcpy(temp, str); //копируем содержимое строки str в temp
puts(temp);
strcpy(temp, “four, five”);
puts(temp);
Использование функции strcpy в программе небезопасно, если источник копируемой строки ненадежен. В приведенном выше примере копируется константная строка, поэтому заранее известно количество копируемых в
temp символов. Однако, если содержимое и размер строки заранее неизвестны (например, если она вводится пользователем с клавиатуры или поступает
вместе с запросом по сети), то использование функции strcpy может привести к проблемам:
char str[100], temp[50];
gets_s(str, 100);
strcpy(temp, str);
puts(temp);
В последнем примере, если пользователь введет больше 49 символов,
то копирование строки str приведет к переполнению строки temp, объем копируемой информации превысит объем зарезервированной под строку памяти и это приведет к изменению смежных с занимаемыми temp областями памяти.
Безопасной версией функции копирования строк является функция
errno_t strcpy_s(char *strDestination, size_t numberOfElements,
const char *strSource );
Функция strcpy_s копирует содержимое в адресе strSource, включая
конечный символ-терминатор строки, в строку-приемник, указанную параметром strDestination. Строка назначения должна быть достаточно велика
для хранения строки источника и его конечного нуль-символа. При перекрытии исходной и конечной строк поведение инструкции strcpy_s не определено.
7
char str[100], temp[50];
gets_s(str, 100);
strcpy_ы(temp, 50, str);
puts(temp);
Если размер копируемой в строку информации заранее неизвестен,
можно воспользоваться еще одной версией копирования строк:
char *strncpy(char *dest, const char *src, int maxlen);
По сравнению с функцией strcpy здесь добавляется еще один целочисленный параметр – maxlen, задающий максимальное количество копируемых
символов.
char str[100], temp[50];
gets_s(str, 100);
strncpy(temp, str, 49);
temp[49]=0;//на случай, когда строка str длиннее 49 символов
// терминатор в строке-приемнике устанавливаем вручную
puts(temp);
Необходимо отметить, что для этой функции также определена безопасная версия strncpy_s:
errno_t strncpy_s(char *strDest,
size_t numberOfElements,
const char *strSource, size_t count );
которая не дает скопировать в строку приемник символов больше, чем
там может поместиться, даже если некорректно задан параметр count.
Все функции для работы со строками принимают в качестве параметров адреса начала обрабатываемых строк. Это позволяет передавать в функцию не только адреса начала строки, но и адреса некоторых важных символов в строке (адрес начала первого слова, адрес определенного символа в
строке и т.п.):
char str[16]=“One, two, three”, char temp[10];
strncpy(temp, str+5, 3); //копируем в temp 3 символа начиная
//с 5-го из str
temp[3]=0;
puts(temp); //на экран выведет two
А можно осуществлять копирование и в рамках одной строки:
char str[16]=“One, two, three”;
strcpy(str+5, str+10);
puts(str); //на экран выведет One, three
Безопасными версиями
Еще одной часто используемой операцией со строками является их
объединение (конкатенация). Операцию можно реализовать с использованием функции strcat:
char *strcat(char *dest, const char *src);
Объединяет исходную строку src и результирующую строку dest, присоединяя первую к последней. Возвращает адрес приемника dest.
8
char str[100]=“One, two, three”;
strcat(str, “, four”);
puts(str); //на экран выведет One, two, three, four
Использование функции strcat также может быть небезопасным, поскольку отсутствует контроль над количеством копируемых символов и результирующая строка способна переполнить приемник. Если количество добавляемых символов неконтролируемо, необходимо использовать функцию
strcat_s:
errno_t strcat_s(char *strDestination, size_t numberOfElements, const char *strSource);
Функция strcat_s добавляет strSource к strDestination, а затем к результирующей строке завершающий символ null. Начальный символ strSource
перезаписывает конечный символ null strDestination. При перекрытии исходной и конечной строк поведение инструкции strcat_s не определено.
char str[100]=“One, two, three”;
strcat_s(str,100, “, four”);
puts(str); //на экран выведет One, two, three, four
Еще одной функцией, позволяющей контролировать количества добавляемых к строке символов, язвлеятся strncat:
char *strncat(char *dest, const char *src, int maxlen);
Эта функция объединяет результирующую строку dest и не более
maxlen символов строки src.
Таким образом, должно соблюдаться неравенство:
strlen ( dest ) + maxlen + 1 ≤ sizeof ( dest )
По аналогии с функцией strncpy, у функции strncat есть безопасный
аналоги strncat_s, который не даст переполниться строке-приемнику даже
при некорректно заданных входных данных.
Функции копирования и конкатенации строк strcpy и strcat возвращают как результат своей работы адрес строки- приемника. Это дает возможность осуществлять каскадные (вложенные) вызовы этих функций:
char *str=new char[40];
strcpy(str,“One, two, three”);
char * str2=new char[20];
strcpy(str2,“four, five”);
strcat(strcat(str,“, “), str2); //*
puts(str); //на экран выведет One, two, three, four, five
…
delete [ ]str;
delete [ ] str2;
В строке, помеченной * в комментарии, сначала объединяются строки
str и ”, ”, а затем результат этого объединения в строке str еще раз конкатенируется, теперь уже с str2.
Важной и очень часто используемой операцией в программе является
процедура сравнения строк. Под сравнением здесь понимается лексикогра9
фическое сопоставление строк, и меньшей при таком сравнении будет та
строка, которая в условном словаре будет стоять выше (раньше). Такое сравнение должно выполняться попарным последовательным сравнением кодов
символов строк слева направо до первого несовпадения или до конца одной
из строк. Очевидно, такое сравнение не может выполнить следующий код:
char str[16], str2[10];
gets_s(str, 16);gets_s(str2, 10);
if (str==str2)
//никогда не выполнится
{…}
В приведенном примере сравниваются не содержимое строк, а адреса
их месторасположения в памяти. Так как это разные строки, то условие никогда не выполнится. Выполнить сравнение строк может функция strcmp:
int strcmp(char *s1, char *s2);
Эта функция сравнивает две строки и возвращает отрицательное значение, если s1<s2; нуль, если s1=s2; положительное значение, если s1>s2. Отношение s1>s2носит лексикографический характер, по которому, например:
“abc”>”aaa”
“aaa”>”aa”
“bcd”>“abc”
“abc”>”Abc”
Используя данную функцию, можно, например, защитить свою программу паролем:
char str[16];
puts(“Введите пароль:”);
gets_s(str, 16);
if (strcmp(str, “мой пароль”)==0)
{ //предоставление доступа
}
else exit(1);
На практике часто можно встретить вариант сравнения, не использующий операции отношения:
if (!strcmp(str, “мой пароль”))
Функция сравнения имеет ряд модификаций:
int stricmp(char *s1, char *s2);
Сравнивает две строки без учета регистра, то есть считая заглавные и
прописные символы одинаковыми (только для латинского алфавита).
int strncmp(char *s1, char *s2, int maxlen);
Сравнивает только первые maxlen символов строк.
int strnicmp(char *s1, char *s2, int maxlen);
Сравнивает только первые maxlen символов строк без учета регистра
(только для латинского алфавита).
Проиллюстрируем работу этих функций примерами:
10
char str1[20]=“Hello, world”;
char str2[20]=“Hello, all”;
char str3[20]=“hello, world”;
cout<<strcmp(str1,str2);
cout<<strncmp(str1,str2,5);
сout<<stricmp(str1,str3);
cout<<strncmp(str3,str2, 5);
cout<<strnicmp(str3,str2,5);
//22 (>0)
//0
//0
//1 (>0)
//0
Операцию поиска подстроки в строке можно реализовать с использованием функции strstr:
char *strstr(char *s1, const char *s2);
Функция ищет в строке s1 строку s2. Возвращает адрес первого символа вхождения строки s2. Если строка отсутствует - возвращает нуль.
char str1[20]=“One, two, three, two”;
char str2[10]=“two”;
cout<<strstr(str1,str2); //на экран выведет two, three, two
В приведенном примере функция strstr найдет первое вхождение строки “two” в str1 и вернет адрес первого символа ‘t’. Далее выводится содержимое строки str с этой позиции.
В состав стандартных библиотек языка Си не входит функция удаления подстроки. Но ее можно реализовать с помощью других функций. Удалить подстроку длинною k символов, начиная с позиции i, можно вызовом
strcpy:
strcpy(str+i, str+i+k);
Если удаляемую подстроку необходимо предварительно найти в строке, используем код:
char *ptc, str2[10]=“two, ”;
char str[30]=“One, two, three”;
ptc=strstr(str, str2); //находим подстроку str2 в str
strcpy(ptc, ptc+strlen(str2)); //удаляем strlen(str2) симво
// лов начиная с позиции ptc
puts(str);
В таблице 1 приведены некоторые дополнительные функции, которые
могут быть использованы для обработки информации в строках
Таблица 1. Функции языка Си для работы со строками
Прототип функции
Выполняемое действие
Ищет в строке s первое вхождение символа c,
char *strchr(const char *s,
начиная с начала строки. В случае успеха
int c);
возвращает указатель на найденный символ,
иначе возвращает нуль.
Аналогично предыдущему, только поиск
char *strrchr(const char *s,
осуществляется с конца строки.
int c);
Возвращает длину максимальной начальной
int strcspn(const char *s1,
подстроки строки s1, не содержащей симвоconst char *s2);
11
лов из второй строки s2.
char *strdup(const char *s); Копирует строку во вновь выделенный блок
памяти, самостоятельно выделяя из кучи
необходимое для размещения копии количество байтов. Возвращает указатель на сдублированную строку. Удалить эту строку можно с помощью операции delete, указав в качестве параметра указатель на эту строку. Если
памяти недостаточно - возвращается нуль.
Преобразует все прописные (большие) буквы
char *strlwr(char *s);
в строчные (малые) в строке s.
Преобразует все строчные (малые) буквы в
char *strupr(char *s);
прописные (большие) в строке s.
Заполняет строку s символами c. Параметр n
char *strnset(char *s, int c,
задает количество размещаемых символов в
int n);
строке.
char *strpbrk(const char *s1, Ищет в строке s1 первое вхождение любого
символа из строки s2. Возвращает указатель
const char *s2);
на первый найденный символ или нуль - если
символ не найден.
Изменяет порядок следования символов в
char *strrev(char *s);
строке на обратный (кроме завершающего
нулевого символа). Функция возвращает
строку s.
Заменяет все символы строки s заданным
char *strset(char *s, int c);
символом c.
Вычисляет длину максимальной начальной
int strspn(const char *s1,
подстроки строки s1, содержащей только
const char *s2);
символы из строки s2.
Еще один тип часто использующихся при работе со строками операций - преобразование числовых данных в строковые и обратно. Функции, реализующие эти операции, представлены в таблице 2.
Таблица 2. Функции языка Си для преобразования числовых данных в
строковые и обратно
Прототип функции
Выполняемое действие
Преобразует строку s в число с плавающей
double atof(const char *s);
точкой типа double
Преобразует строку s в число типа int. Возint atoi(const char *s);
вращает значение или нуль, если строку преобразовать нельзя.
char *itoa(int value, char *s, Преобразует значение целого типа value в
строку s. Возвращает указатель на результиint radix);
рующую строку. Значение radix - основание
системы счисления, используемое при преоб12
char *ecvt(double value, int
ndig, int *dec, int *sign);
разовании (от 2 до 36)
Преобразует значение value типа double в завершающуюся нулем строку. Возвращает адрес статического буфера, который перезаписывается при каждом вызове этой функции.
Чтобы сохранить результат, можно воспользоваться, например, функцией strcpy(). Значение ndig – требуемое количество цифр результата. Значение dec - указатель на целое
значение, где размещается позиция десятичной точки (результирующая строка не содержит символа десятичной точки). Если
число отрицательное, sign получает значение
1, если положительное – 0.
Для преобразования данных из числового типа в строку можно использовать также функцию sprintf, а для обратного – sscanf:
char str[10], str1[10];
int x=-30;
double y=123.45678;
sprintf(str, “%d”, x);
puts(str);
//выведет -30
sprintf(str1, “%5.2lf”, y);
puts(str1);
//выведет 123.46
При работе со строками зачастую встает задача разбиения строки на
лексемы – подстроки с использованием одного или нескольких разделителей.
Так, например, может быть поставлена задача разбить строку на предложения – тогда разделителями будут служить знаки пунктуации, завершающие
предложения: '.’, ‘!’, ‘?’. Если же встанет задача разбить предложения на
слова, то в качестве разделителей придется использовать символы ' ’, ‘,’, ‘:’,
'-’, ‘»’, ‘”’, ‘;’. Разбиение строки на лексемы можно выполнить алгоритмически, отыскивая в строке символы-разделители и занося подстроки, заключенные между ними в отдельные элементы массива строк-лексем. Но гораздо
удобнее будет воспользоваться специализированной функцией strtok, которая выполнит подобное разбиение автоматически. Эта функция имеет следующий прототип:
char * strtok( char * string, const char * delims );
Как уже упоминалось, эта функция ищет в строке string лексемы, представляющие собой последовательность символов, разделенных знакамиразделителями. Для поиска последовательности лексем функцию strtok
необходимо вызвать несколько раз, каждый вызов будет возвращать адрес
очередной найденной лексемы.
При первом вызове функции необходимо передать адрес строки для
поиска в качестве первого аргумента (string). Функция начинает поиск с пер13
вого символа переданной строки. При последующих вызовах функции передается нулевой указатель, что заставляет ее искать следующую лексему
начиная с позиции окончания последней найденной лексемы. Позиция окончания лексемы определяется по совпадению очередного символа строки
string с одним из символов строки разделителей delims. Этот конечный маркер лексемы заменяется нулевым символом, и адрес лексемы возвращается
функцией. Следующий вызов функции strtok начинаются нулевого символа маркера конца предыдущей лексемы. Если функция strtok при очередном
вызове не найдет ни одной лексемы – она вернет нулевой указатель. Из приведенного описания понятно, что исходная строка string при обработке
функцией strtok претерпевает изменения (в нее добавляются множество символов-терминаторов для обозначения конца лексемы), поэтому, возможно,
имеет смысл сначала сделать копию строки для использования в функции
strtok. Рассмотрим пример разбиения строки на отдельные слова с использованием функции strtok:
char str[51];
char seps[]=" ,.!?-\n";
puts("Введите строку");
gets_s(str, 50);
char *pWord;
//находим первую лексему
pWord = strtok(str, seps);
//пока находятся новые лексем (слова в строке)
while (pWord) {
//выводим лексему на экран
puts(pWord);
//ищем следующую лексему
pWord = strtok(NULL, seps);
}
//Выводм на экран исходную строку
puts(str);
Если запустить этот код и ввести с клавиатуры строку
Делу время – потехе час!
На экране получим список слов этой строки:
Делу
время
потехе
час
Делу
Необходимо обратить внимание, что при попытке вывести исходную
строку после ее обработки функцией strtok, получаем только первое слово.
Это связано с тем, что функция заменила вхождения в строку разделителей
из строки seps на символы конца строки.
Приведенные выше определения и примеры ориентированы на работу с
кодировкой символов ASCII, где каждый символ кодируется одним байтом.
14
Однако, эта кодировка постепенно перестает быть общеупотребимой, уступая более универсальным, многобайтовым. Примером здесь может служить
кодировка Unicode. Стандарт Unicode был предложен некоммерческой организацией Unicode Consortium, образованной в 1991 г. Для представления
каждого символа в этом стандарте используются два байта, что позволяет закодировать очень большое число символов из разных письменностей: в документах Unicode могут соседствовать русские, латинские, греческие буквы,
китайские иероглифы и математические символы.
Для хранения символов Unicode необходимо два байта, из-за чего их
называют широкими символами (wide characters). В языке Си им соответствует тип wchar_t.
wchar_t wstr=L”Hello”;
Объявленная строка wstr в общем случае может иметь разный размер в
различных компиляторах, при программировании под Win32 в средах C++
Builder и Microsoft Visual Studio она будет иметь размер 12 байт (2 байта на
каждый символ и двухбайтовый терминатор). Лексемные широкие строки
предваряются символом L : L ”содержимое строки ”. Для работы с широкими символами в библиотеке string.h имеется набор специальных функций,
схожих по названию и выполняемым действиям рассмотренным выше однобайтным вариантам: wcscat (конкатенация строк), _wcspcpy (копирования
строк), wcscmp (сравнение строк) и т.п. Полный перечень функций для работы с многобайтовыми строками можно посмотреть в документации по
библиотеке string.h.
Рассмотренные в настоящих указаниях средства работы со строками
являются базовыми для языка Си. На их основе разработаны и широко используются классы, облегчающие программисту работу со строками (например, string в библитеке STL или AnsiString в VCL). Однако, в их основу положена объектно-ориентированная парадигма, знакомство с которой запланировано на старших курсах, где вы сможете расширить свой арсенал
средств строковой обработки данных.
2. Порядок выполнения работы
1. Ознакомьтесь с теоретическими основами работы со строками на языке
Си в настоящих указаниях и конспектах лекций.
2. Получите вариант задания у преподавателя.
3. Составьте алгоритм решения задачи согласно варианту задания, оформите его в графической форме.
4. Используя разработанный алгоритм, напишите программу.
5. Отладьте разработанную программу и покажите результаты работы
программы преподавателю.
6. Составьте отчет по лабораторной работе.
7. Отчитайте работу преподавателю.
15
3. Содержание отчета
Отчет по лабораторной работе должен содержать следующие сведения:
- название и цель работы;
- вариант задания;
- графическую схему алгоритма решения задачи;
- листинг разработанной программы с комментариями;
- результаты работы программы.
4. Пример оформления отчета:
Задание
С клавиатуры вводятся две строки. Определить, сколько раз вторая строка
входит в первую.
4.1. Схема алгоритма решения задачи.
16
начало
Ввод строки 1 (str)
Ввод строки 2 (str1)
bFind=1, Count=0
tmp=str
нет
bFind=1
да
tmp=strstr(str,str1)
да
нет
tmp≠0
Count++
tmp++
bFind=0
Вывод Count
конец
Рис.1. Блок-схема алгоритма решения задачи
4.2. Листинг программы.
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
main()
{
char str[1000],str1[100],*tmp;
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
int Count=0, bFind=1;
clrscr();
puts("Введите 1-ю строку\n");
gets_s(str, 1000);
17
puts("Введите 2-ю строку\n");
gets_s(str1, 100);
Count=0;
tmp=str; //указатель tmp пробегает по всем символам строки str
while(bFind)
//пока есть вхождения str2 в str
{
tmp=strstr(tmp, str1); //ищем очередное вхождение
if(tmp)
{
Count++;
tmp++;
//следующее вхождение ищем за уже найденным
}
else
bFind=0; //не найдено совпадений – завершаем цикл
}
cout<<Count;
getch();
return 0;
}
4.3. Результаты работы программы.
18
5. Варианты заданий
Вариант №1
Написать программу, которая считывает три предложения и выводит их в
обратном порядке.
Вариант №2
Написать программу, которая считывает текст и выводит на экран только
предложения, содержащие введенное с клавиатуры слово.
Вариант №3
Написать программу, которая считывает текст и выводит на экран только
строки, содержащие двузначные числа.
Вариант №4
Написать программу, которая считывает английский текст и выводит на
экран слова, начинающиеся с гласных букв.
Вариант №5
Написать программу, которая считывает текст и выводит его на экран, ме
няя местами каждые два соседних слова.
Вариант №6
Написать программу, которая считывает текст и выводит на экран только
предложения, не содержащие запятых.
Вариант №7
Написать программу, которая считывает текст и определяет, сколько в нем
слов, состоящих не более чем из четырех букв.
Вариант №8
Написать программу, которая считывает текст и выводит на экран только
предложения, то есть предложения, заключенные в кавычки.
Вариант №9
Написать программу, которая считывает текст и выводит на экран только
предложения, состоящие из заданного количества слов.
Вариант №10
Написать программу, которая считывает английский текст и выводит на
экран слова текста, начинающиеся и оканчивающиеся на гласные буквы.
Вариант №11
Написать программу, которая считывает текст и выводит на экран только
строки, не содержащие двузначных чисел.
19
6. Контрольные вопросы
1. В какой форме можно представить строковую информацию в языке
Си?
2. Что такое терминатор строки и какую роль он играет при обработке
данных в строке?
3. Почему при вводе информации в строку предпочтительнее использовать функцию fgets?
4. Почему использование функций strcpy и strcat может быть потенциально опасным для программы?
5. Предложите фрагмент программы, удаляющий часть строки до первого пробела.
6. Предложите фрагмент программы, копирующий первые 5 символов
одной строки в конец другой.
7. Что такое лексикографический порядок следования строк? Как реализуется лексикографическое сравнение строк в языке Cи?
7. Литература
1. Березин Б. И., Березин С. Б. Начальный курс С и С++. М.: ДиалогМИФИ, 2007 г., - 288с.
2. Демидович Е.И. Основы алгоритмизации и программирования. Язык
Си. Учебник для вузов. СПб.:BHV, 2008 г. – 439с.
3. Костюкова Н. И., Калинина Н. А. Язык Си и особенности работы с ним.
М: Бином. Лаборатория знаний, 2006 г. – 208с.
4. Страуструп Б. Язык программирования С++. Специальное издание.
СПб.: Бином, 2008 г., 1104с.
5. Фомин С.С. Подбельский В.В. Программирование на языке Си: Учебное пособие. М.:Финансы и статистика, 2007 г. – 600с.
6. Шилдт Г. Полный справочник по C++. М.: Вильямс, 2007 г., - 800с.
20
Дмитрий Николаевич Лясин
Марина Викторовна Фадеева
Работа со строками в языке Си. Методические указания к лабораторным работам по курсу «Основы программировнаия».
План выпуска электронных изданий 2011г., поз. N___
Подписано “На выпуск в свет ______”. Уч. -изд.л.
На магнитном носителе.
Волгоградский государственный технический университет.
400131 Волгоград , пр. Ленина , 28.
21
Download