Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Основы программирования. Борисенко

.pdf
Скачиваний:
1534
Добавлен:
09.04.2015
Размер:
9.31 Mб
Скачать

Определение типов символов

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.