Основы программирования. Борисенко
.pdfОпределение типов символов |
221 |
int |
numLines =0; |
// Суммарное |
число |
строк |
:= 0 |
int |
numWords =0; |
// Суммарное |
число |
слов |
:= 0 |
bool |
readingWord |
= false; // Читаем слово := |
false |
printf("Введите имя файла: " ) ; scanf('7„s", fileName);
f |
= fopen(fileName, "rb"); |
// Открываем файл |
|
i f |
(f == 0) { // При ошибке |
открытия файла |
|
|
// Напечатать сообщение |
об ошибке |
|
|
p e r r o r O ' ^ могу открыть |
файл |
для чтения"); |
|
return 1; // закончить |
работу |
программы с кодом 1 |
} |
|
|
|
|
|
|
|
|
|
while |
((c = fgetc(f)) != EOF) { // Читаем |
символ |
|||||||
// Цикл продолжается, пока c != -1 (конец файла) |
|||||||||
++numChars; // Увеличиваем |
число |
символов |
|||||||
// |
Подсчитываем |
число |
символов |
перевода строки |
|||||
i f |
(c == '\n') { |
|
|
|
|
|
|
||
} |
++numLines; |
// Увеличиваем |
число строк |
||||||
|
|
|
|
|
|
|
|
|
|
// |
Подсчитываем |
число |
слов |
|
|
|
|
|
|
i f |
(!isspace(c)) { |
// если |
|
c не пробел |
|||||
|
i f |
(!readingWord) |
{ // |
если |
не читаем слово |
||||
|
|
++numWords; |
// |
увеличить |
число слов |
||||
|
} |
readingWord = true; |
// читаем |
с л о в о : = ^ ^ |
|||||
|
|
|
// |
конец |
если |
||||
} |
else |
{ |
|
// иначе |
слово:=false |
||||
} |
readingWord |
= f a l s e ; / / |
читаем |
||||||
|
|
|
// конец если |
|
|||||
} |
|
|
|
|
|
|
|
|
|
f c l o s e ( f ) ;
222 |
|
3.9. Технология программирования на Си |
|
// Печатаем результат |
|
||
р ^ ^ ^ " Ч и с л о |
символов в файле = /d\n", numChars); |
||
р ^ ^ ^ " Ч и с л о |
слов |
= /d\n", |
numWords); |
printf("Число |
строк |
= /d\n", |
numLines); |
return 0; // Возвращаем код успешного завершения
}
Пример выполнения программы wc2 в применении к собственному тексту, записанному в файле "wc2.cpp":
Введите имя файла: wc2.cpp Число символов в файле = 1998 Число слов = 260 Число строк = 57
Р а б о та с текстовыми строками
Стандартная библиотека Си предоставляет средства вычисления длины строки, копирования, сравнения, соединения (конкатенации) строк, поиска вхождений одной строки в другую. Ф у н к ц и и описаны в стандартном заголовочном файле "string.h". Прототипы наиболее часто используемых функций приведены ниже.
Определение длины строки
size_ t strlen(const char * s ) ; длина строки.
Копирование строк
char *strcpy(char *dst, const char *src); копировать строку src в строку dst;
char *strncpy(char *dst, const char *src, size_ t maxlen);
копировать строку src в dst, не более maxlen символов;
char *strcat(char *dst, const char *src); копировать строку src в конец dst (конкатенация строк).
Работа с произвольными массивами |
байтов |
void *memmove(void *dst, const void *src, size_t len);
Пример: программа «Записная книжка» |
|
|
|
|
|
223 |
|||||
|
копировать область |
памяти с адресом |
src размером |
len |
байтов |
||||||
|
в область памяти с адресом dst; |
|
|
|
|
|
|
||||
void |
*memset(void |
*dst, i n t value, size_t len); |
записать |
||||||||
|
значение value в к а ж д ы й |
из len байтов, начиная с адреса |
dst. |
||||||||
Сравнение |
строк |
|
|
|
|
|
|
|
|
|
|
int |
strcmp(const char |
*s1, const |
char |
*s2); |
лексикографи |
||||||
|
ческое |
сравнение |
строк |
s i и |
s2. Результат |
нулевой, |
если |
||||
|
строки |
равны, |
отрицательный, |
если |
первая |
строка |
меньше |
||||
|
второй, и положительный, если первая строка больше второй; |
||||||||||
int |
strncmp(const |
char *s1, const |
char |
*s2, size _ t maxlen); |
|||||||
|
сравнение строк s i и s2, сравнивается |
не более maxlen |
симво¬ |
||||||||
|
лов; |
|
|
|
|
|
|
|
|
|
|
int memcmp(const void *m1, const void *m2, size_ t len);
сравнение областей памяти с адресами m i и m 2 размером len каждая .
Поиск
char |
*strchr(const char |
*s, i n t c ) ; найти |
первое вхождение |
||||
|
символа c в строку s. Ф у н к ц и я |
возвращает |
указатель |
на най¬ |
|||
|
денный символ или ноль в случае |
неудачи; |
|
|
|
||
char |
*strstr(const char |
*s1, const |
char *s2); найти |
первое |
|||
|
вхождение строки s2 |
в строку |
s i . Ф у н к ц и я |
возвращает ука¬ |
|||
|
затель на найденную |
подстроку |
в s i , равную |
строке |
s2, ил и |
ноль в случае неудачи.
Пример: программа «Записная книжка»
Вкачестве примера работы с текстовой информацией рассмот рим программу «Записная книжка» . Записная книжка хранит имена людей и их телефоны. Под именем понимается полное имя, включа¬ ющее фамилию, имя, отчество в произвольном формате: им я пред¬ ставляется любой текстовой строкой. Телефон также представляется
224 |
3.9. Технология программирования на Си |
|
текстовой строкой (представление с помощью целого |
числа не под¬ |
|
ходит, потому |
что номер может быть очень длинным |
и содержать |
специальные символы, например, знак плюс). |
|
|
Программа |
позволяет добавлять записи в записную |
книжку, уда |
лять записи и искать номер телефона по имени. При поиске не обяза¬ тельно вводить им я полностью, достаточно нескольких первых сим¬ волов. М о ж н о т а к ж е распечатать содержимое всей записной книжки . В перерывах между запусками программы информация сохраняется
в файле |
"NoteBook.dat". |
|
|
|
|
|
|
Записная книжка соответствует структуре данных |
«нагруженное |
||||||
множество», которая будет рассмотрена в разделе 4.6.1 |
|
и для |
которой |
||||
имеется ряд реализаций, обеспечивающих |
быстрый поиск и модифи¬ |
||||||
кацию. М ы , однако, ограничимся сейчас |
простейшей |
реализацией: |
|||||
не упорядочиваем записи по алфавиту и применяем |
последователь¬ |
||||||
ный поиск. Записи хранятся |
в массиве, |
его максимальный |
размер |
||||
равен 1000 элементов. Новая |
запись добавляется в конец |
массива. |
|||||
При удалении записи из к н и ж к и последняя |
запись |
переписывается |
|||||
на место |
удаленной. |
|
|
|
|
|
|
Д л я |
записей используется |
структурный |
тип NameRecord, опре¬ |
деленный следующим образом:
typedef struct { char *name; char *phone;
} NameRecord;
Здесь поле name структуры является указателем на им я абонен¬ та, которое представляет собой текстовую строку. Номер телефона
также задается строкой, |
указатель |
на которую записывается |
в по¬ |
||
ле phone. Пространство под строки |
захватывается |
в динамической |
|||
памяти с помощью функции malloc . При удалении |
записи |
память |
|||
освобождается с помощью функции |
free. |
|
|
||
Записи хранятся в массиве records : |
|
|
|||
const |
i n t MAXNAMES |
= 1000; |
|
|
|
s t a t i c |
NameRecord |
records[MAXNAMES]; |
|
|
|
s t a t i c |
i n t numRecords = 0; |
|
|
|
Пример: программа «Записная книжка» |
225 |
|
Константа MAXNAMES |
задает максимально возможный размер мас¬ |
сива records. Текущее количество записей (т.е. реальный размер мас¬ сива) хранится в переменной numRecords.
В начале работы программа вводит содержимое записной к н и ж к и
из файла |
"NoteBook.dat", |
им я которого |
задается ка к константный |
указатель |
на константную |
строку: |
|
const |
char* const NoteBookFile |
= "NoteBook.dat"; |
В конце работы содержимое записной к н и ж к и сохраняется в файле.
Д л я ввода |
используется |
функция loadNoteBook, дл я сохранения — |
|
функция saveNoteBook |
с прототипами |
||
s t a t i c |
bool |
loadNoteBook(); |
|
s t a t i c |
bool |
saveNoteBook(); |
Каждой записи соответствует пара строк файла. Пусть, например, имя абонента "Иван Петров", телефон — "123-45-67". Этой записи соответствует пара строк
name=MBaH Петров phone=123-45-67
Записи в файле разделяются пустыми строками.
Сохранение файла выполняется только в случае, когда содержи¬ мое записной к н и ж к и изменялось в процессе работы. За это отвечает
переменная |
|
s t a t i c bool |
modified; |
которая принимает |
значение true , если хотя бы раз была выполнена |
одна из команд, меняющих содержимое записной к н и ж к и . |
Работа с записной книжкой происходит в интерактивном режиме. Пользователь вводит команду, программа ее выполняет. При выпол¬ нении команды программа просит ввести дополнительную информа¬ цию, если это необходимо, и печатает результат выполнения. Коман¬ ды записываются латинскими буквами, чтобы исключить возможные проблемы с русскими кодировками. После команды может идти им я абонента. Реализованы следующие команды:
226 |
3.9. Технология |
программирования на Си |
|
add — добавить запись. |
Программа просит ввести имя абонента, ес¬ |
||
ли оно не указано |
непосредственно после |
команды, |
затем его |
телефон, после этого запись добавляется |
в записную |
книжку, |
или телефон изменяется, если данное им я у ж е содержится в книжке;
remove — удалить запись. Программа просит ввести им я абонента, если оно не указано непосредственно после команды, и удаляет запись из книжки, если она там присутствует;
find |
— найти запись. Программа при необходимости просит ввести |
|||
|
имя абонента и печатает либо его телефон, либо |
сообщение, |
||
|
что данного |
имени в к н и ж к е нет; |
|
|
modify — изменить номер телефона. Программа просит |
ввести им я |
|||
|
абонента, если оно не указано непосредственно после |
команды. |
||
|
Затем осуществляется поиск записи с данным именем. В случае |
|||
|
успеха программа просит ввести новый номер телефона, после |
|||
|
этого старый |
номер заменяется на новый; в противном случае, |
||
|
печатается сообщение, что имя не содержится в книжке; |
|||
show — напечатать записи, для которых имена начинаются |
с данно¬ |
|||
|
го префикса. Если имя или начало имени не указано |
непосред¬ |
||
|
ственно после команды, то печатается все содержимое |
записной |
||
|
книжки . Если указано начало имени, то печатаются все записи, |
|||
|
для которых начало имени совпадает с заданным; |
|
|
|
help — напечатать |
подсказку; |
|
|
|
quit |
— закончить |
работу. |
|
|
Каждой команде соответствует отдельная функция, выполняющая команду. Например, команде add соответствует функция onAdd , ко¬ манде find — функция onFind и т.д. Начало искомого имени и его длина, если имя указано в командной строке, передаются через ста¬ тические переменные
s t a t i c char namePrefix[256]; s t a t i c i n t namePrefixLen;
Пример: программа «Записная книжка» |
|
|
|
|
227 |
|||
Если им я не указано в командной |
строке, |
то дл я его ввода вызы¬ |
||||||
вается функция readName , которая |
просит |
пользователя ввести им я |
||||||
с клавиатуры, считывает |
им я и заполняет переменные |
namePrefix и |
||||||
namePrefixLen. |
|
|
|
|
|
|
|
|
Д л я |
поиска |
записи |
с заданным началом |
имени |
используется |
|||
функция |
search |
с прототипом |
|
|
|
|
|
|
s t a t i c i n t search( |
// Начало |
искомого |
имени |
|||||
|
const |
char *namePrefix, |
||||||
); |
int startPosition |
// Позиция начала |
поиска |
|||||
|
|
|
|
|
|
|
|
Ей передается начало искомого имени и индекс элемента массива, с которого начинается поиск. Второй аргумент нужен дл я того, чтобы последовательно находить имена с одинаковым префиксом (напри¬ мер, имена, начинающиеся с заданной буквы). Ф у н к ц и я возвращает индекс найденного элемента в случае успеха или отрицательное зна¬ чение —1 при неудаче. Применяется последовательный поиск, при ко¬ тором просматриваются все элементы, начиная со стартовой позиции. Д л я сравнения имен используется стандартная функция strncmp(s1, s2, n), которая сравнивает n первых символов строк s1 и s2. При поиске в качестве s1 используется заданное начало имени, в каче¬
стве s2 |
— очередное имя из записной книжке, n равно длине начала |
||
имени. |
|
|
|
Д л я |
ввода строки с клавиатуры мы используем вспомогательную |
||
функцию readLine с прототипом |
|
||
s t a t i c bool |
readLine( |
|
|
); |
char *buffer, i n t maxlen, i n t *len |
|
|
|
|
|
|
Ф у н к ц и я вводит |
строку из стандартного входного потока, исполь¬ |
||
зуя библиотечную |
функцию fgets. Затем из конца строки |
удаляются |
|
символы-разделители строк '\r' и '\n'. Введенная строка |
помещается |
||
в массив |
buffer с максимальной длиной maxlen. Реальная длина вве¬ |
||
денной строки записывается в переменную, адрес которой |
передается |
||
через указатель len . |
|
||
Полный текст |
программы: |
|
228 |
3.9. Технология программирования на Си |
//Файл "NoteBook.cpp"
//Программа "Записная книжка"
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h>
typedef struct {
char *name; // Указатель на имя char *phone; // Указатель на телефон
} NameRecord;
// Максимальный размер записной книжки const i n t MAXNAMES = 1000;
// Массив записей
static NameRecord records[MAXNAMES];
// Текущее количество записей в книжке static i n t numRecords = 0;
// Имя файла для сохранения содержимого книжки const char* const NoteBookFile = "NoteBook.dat";
static |
bool modified; |
// Начало |
имени, |
||
static |
char |
namePrefix[256]; |
|||
static |
i n t namePrefixLen; |
// |
его |
длина |
|
static |
char |
phone[256]; |
// Телефон, |
||
static |
i n t phoneLen; |
// |
его |
длина |
|
// Прототипы |
функций |
// Загрузить книжку из файла |
|||
static |
bool |
loadNoteBook(); |
|||
static |
bool |
saveNoteBook(); |
// Сохранить книжку в файле |
||
static |
bool |
readLine( |
// Ввести строку с клавиатуры |
||
char * l i n e , i n t maxlen, |
int |
*len |
|
||
); |
|
|
|
|
|
Пример: программа «Записная книжка» |
229 |
s t a t ic |
bool readName(); |
// Ввести имя с клавиатуры |
|||
static |
i n t search( |
// Поиск имени в массиве |
|||
const char *namePrefix, |
// |
начало |
искомого |
имени |
|
int |
startPosition |
// |
позиция |
начала |
поиска |
); |
|
|
|
|
|
static void releaseMemory(); // Освободить память
// Прототипы функций, реализующих команды static void onAdd();
static void onRemove(); static void onFind(); static void onModify(); static void onShow(); static void onHelp();
int main() { |
// Введенная строка |
||||
char |
line[256]; |
||||
int |
lineLen; |
// Длина |
строки |
|
|
int |
comBeg, |
// Индексы начала |
команды |
||
int |
comEnd; |
// |
и |
за-конца |
|
comLen; |
// Длина |
команды |
|
||
const char *command; |
// Указатель на начало команды |
||||
int |
nameBeg; |
// Индекс |
начала |
имени |
|
int |
i ; |
// |
Индекс |
в массиве line |
printf("Прогрaммa |
\"Записная |
книжкa\"\n"); |
||
onHelp(); |
// Напечатать подсказку |
|||
loadNoteBook(); // Загрузить |
содержимое книжки |
|||
while |
// |
|
|
из файла |
(true) { |
// Приглашение на ввод команды |
|||
printf(">"); |
||||
// Ввести командную |
строку |
|||
i f |
(!readLine(line, |
256, &lineLen)) { |
230 |
3.9. Технология программирования на Си |
} |
break; |
// При ошибке |
завершить |
программу |
|||||
|
|
|
|
|
|
|
|
|
|
// Разбор |
командной строки |
|
|
||||||
// |
1. Пропустить пробелы |
в начале строки |
|
||||||
i |
= 0; |
|
|
|
|
|
|
|
|
while |
( i < lineLen |
&& |
isspace(line[i])) |
{ |
|||||
} |
|
|
|
|
|
|
|
|
|
// 2. Выделить |
команду |
|
|
|
|
||||
comBeg |
= i ; |
|
|
|
|
|
|
||
while |
( i < lineLen |
&& |
i s a l p h a ( l i n e [ i ] ) ) |
{ |
|||||
} |
|
= i ; |
|
|
|
|
|
|
|
comEnd |
+ comBeg; |
// Указ.начала |
команды |
||||||
command = l i n e |
|||||||||
comLen = comEnd - comBeg; |
// ее длина |
|
|||||||
i f |
(comLen |
<= |
0) |
|
|
// Команда |
пустая => |
||
|
continue; |
|
|
|
// ввести следующую |
||||
// 3. Выделить |
префикс |
имени |
|
|
|||||
i |
= comEnd; |
|
|
|
|
|
|
||
// Пропустить пробелы перед именем |
|
{ |
|||||||
while |
( i < lineLen |
&& |
isspace(line[i])) |
||||||
} |
|
|
i ; |
|
|
|
|
|
|
nameBeg = |
lineLen) { |
|
|
||||||
i f |
(nameBeg >= |
|
|
||||||
|
// Имя не указано в команде |
что имя |
|||||||
|
namePrefix[0] |
=0; |
// Запомним, |
||||||
|
namePrefixLen |
= 0; |
// |
пустое |
}else {
//Имя задано в команде, запомним его.
//Указ. на начало имени равен line+nameBeg,
//длина имени равна lineLen-nameBeg.