Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

СОЦИОЛОГИЯ. Лекция 7, 8. Некоторые приемы программирования

Лекция 7, 8. Некоторые приемы программирования. Поиск количества, суммы, произведения, максимума, минимума. Смена местами содержимого двух ячеек памяти. Работа с одномерными массивами. Работа с многомерными массивами

ФАЙЛЫ

Указатели на функции

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

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

Через указатель можно войти в функцию, т.е. запустить ее на выполнение. Объявление вида:

int (*f)();

говорит о том, что f - это указатель на функцию, возвращающую целое значение. Первая пара скобок необходима, без них int *f(); означало бы, что f - функция, возвращающая указатель на целое значение. После объявления указателя на функцию в программе можно использовать объекты: *f - сама функция; f - указатель на функцию. Для любой функции ее имя (без скобок и аргументов) является указателем на эту функцию.

Файлом называют способ хранения информации на физическом устройстве. Файл - это понятие, которое применимо ко всему - от файла на диске до терминала.

В языке Си отсутствуют операторы для работы с файлами. Все необходимые действия выполняются с помощью функций, включенных в стандартную библиотеку. Они позволяют работать с различными устройствами, такими, как диски, принтер, коммуникационные каналы и т.д. Эти устройства сильно отличаются друг от друга. Однако файловая система преобразует их в единое абстрактное логическое устройство, называемое потоком.

В Си существует два типа потоков: текстовые (text) и двоичные (binary).

Текстовый поток - это последовательность символов. При передаче символов из потока на экран, часть из них не выводится (например, символ возврата каретки, перевода строки).

Двоичный поток - это последовательность байтов, которые однозначно соответствуют тому, что находится на внешнем устройстве.

Прежде чем читать или записывать информацию в файл, он должен быть открыт и тем самым связан с потоком. Это можно сделать с помощью библиотечной функции fopen(). Она берет внешнее представление файла (например, c:\my_prog.txt) и связывает его с внутренним логическим именем, которое используется далее в программе. Логическое имя - это указатель на требуемый файл. Его необходимо определить; делается это, например, так:

FILE *fp;

Здесь FILE - имя типа, описанное в стандартном заголовочном файле stdio.h, fp - указатель на файл. Обращение к функции fopen() в программе осуществляется выражением:

fp = fopen(спецификация файла, "способ использования файла");

Спецификация файла (т.е. имя файла и путь к нему) может, например, иметь вид: "c:\\my_prog.txt" - для файла my_prog.txt на диске с:.

Способ использования файла задается следующими символами:

r - открыть существующий файл для чтения;
w - создать новый файл для записи (если файл с указанным именем существует, то он будет переписан);
а - дополнить файл (открыть существующий файл для записи информации, начиная с конца файла, или создать файл, если он не существует);

r+ - открыть существующий файл для чтения и записи;
w+ - создать новый файл для чтения и записи;
a+ - дополнить или создать файл с возможностью чтения и записи;

rb - открыть двоичный файл для чтения;
wb - создать двоичный файл для записи;
аb - дополнить двоичный файл;

r+b - открыть двоичный файл для чтения и записи;
w+b - создать двоичный файл для чтения и записи;
а+b - дополнить двоичный файл с предоставлением возможности чтения и записи;

rt - открыть текстовой файл для чтения;
wt - создать текстовый файл для записи;
at - дополнить текстовый файл;

r+t - открыть текстовой файл для чтения и записи;
w+t - создать текстовый файл для чтения и записи;
a+t - дополнить текстовый файл с предоставлением возможности записи и чтения.

Если режим t или b не задан (например, r, w или а), то он определяется значением глобальной переменной _fmode. Если fmode=0_BINARY, то файлы открываются в двоичном режиме, а если _fmode=0_TEXT - в текстовом режиме. Константы 0_BINARY и 0_ТЕXТ определены в файле fcntl.h.

Строки вида r+b можно записывать и в другой форме: rb+.

Если в результате обращения к функции fopen() возникает ошибка, то она возвращает константу NULL.

Рекомендуется использовать следующий способ открытия файла:

if ((fp = fopen("c:\\my_prog.txt", "rt")) == NULL)

{

puts("Открыть файл не удалось\n");

exit(1);

}

После окончания работы с файлом он должен быть закрыт. Это делается с помощью библиотечной функции fclose(). Она имеет следующий прототип:

int fclose(FILE *fp);

При успешном завершении операции функция fclose() возвращает значение нуль. Любое другое значение свидетельствует об ошибке.

Рассмотрим другие библиотечные функции, используемые для работы с файлами (все они описаны в файле stdio.h):

1. Функция putc() записывает символ в файл и имеет следующий прототип:

int putc(int с, FILE *fp);

Здесь fp - указатель на файл, возвращенный функцией fopen(), с - символ для записи (переменная с имеет тип int, но используется только младший байт). При успешном завершении putc() возвращает записанный символ, в противном случае возвращается константа EOF. Она определена в файле stdio.h и имеет значение -1.

2. Функция getc() читает символ из файла и имеет следующий прототип:

int getc(FILE *fp);

Здесь fp - указатель на файл, возвращенный функцией fopen(). Эта функция возвращает прочитанный символ. Соответствующее значение имеет тип int, но старший байт равен нулю. Если достигнут конец файла, то getc() возвращает значение ЕОF.

3. Функция feof() определяет конец файла при чтении двоичных данных и имеет следующий прототип:

int feof(FILE *fp);

Здесь fp - указатель на файл, возвращенный функцией fopen(). При достижении конца файла возвращается ненулевое значение, в противном случае возвращается 0.

4. Функция fputs() записывает строку символов в файл. Она отличается от функции puts() только тем, что в качестве второго параметра должен быть записан указатель на переменную файлового типа.

Например:

fputs("Ехаmple", fp);

При возникновении ошибки возвращается значение EOF.

5. Функция fgets() читает строку символов из файла. Она отличается от функции gets() тем, что в качестве второго параметра должно быть указано максимальное число вводимых символов плюс единица, а в качестве третьего - указатель на переменную файлового типа. Строка считывается целиком, если ее длина не превышает указанного числа символов, в противном случае функция возвращает только заданное число символов.

Рассмотрим пример:

fgets(string, n, fp);

Функция возвращает указатель на строку string при успешном завершении и константу NULL в случае ошибки либо достижения конца файла.

6. Функция fprintf() выполняет те же действия, что и функция printf(), но работает с файлом. Ее отличием является то, что в качестве первого параметра задается указатель на переменную файлового типа.

Например:

fprintf(fp, "%х",а);

7. Функция fscanf() выполняет те же действия, что и функция scanf(), но работает с файлом. Ее отличием является то, что в качестве первого параметра задается указатель на переменную файлового типа.

Например:

fscanf(fp, "%х", &a);

При достижении конца файла возвращается значение EOF.

8. Функция fseek() позволяет выполнять чтение и запись с произвольным доступом и имеет следующий прототип:

int fseek(FILE *fp, long count, int access);

Здесь fp - указатель на файл, возвращенный функцией fopen(), count - номер байта относительно заданной начальной позиции, начиная с которого будет выполняться операция, access - способ задания начальной позиции.

Переменная access может принимать следующие значения:

0 - начальная позиция задана в начале файла;
1 - начальная позиция считается текущей;
2 - начальная позиция задана в конце файла.

При успешном завершении возвращается нуль, при ошибке - ненулевое значение.

9. Функция ferror() позволяет проверить правильность выполнения последней операции при работе с файлами. Имеет следующий прототип:

int ferror(FILE *fp);

В случае ошибки возвращается ненулевое значение, в противном случае возвращается нуль.

10. Функция remove() удаляет файл и имеет следующий прототип:

int remove(char *file_name);

Здесь file_name - указатель на строку со спецификацией файла. При успешном завершении возвращается нуль, в противном случае возвращается ненулевое значение.

11. Функция rewind() устанавливает указатель текущей позиции в начало файла и имеет следующий прототип:

void rewind(FILE *fp);

12. Функция fread() предназначена для чтения блоков данных из потока. Имеет прототип:

unsigned fread(void *ptr, unsigned size, unsigned n, FILE *fp);

Она читает n элементов данных, длиной size байт каждый, из заданного входного потока fp в блок, на который указывает указатель ptr. Общее число прочитанных байтов равно произведению n*size. При успешном завершении функция fread() возвращает число прочитанных элементов данных, при ошибке - 0.

13. Функция fwrite() предназначена для записи в файл блоков данных. Имеет прототип:

unsigned fwrite(void *ptr, unsigned size, unsigned n, FILE *fp);

Она добавляет n элементов данных, длиной size байт каждый, в заданный выходной файл fp. Данные записываются с позиции, на которую указывает указатель ptr. При успешном завершении операции функция fwrite() возвращает число записанных элементов данных, при ошибке - неверное число элементов данных.

В языке Си имеются пять стандартных файлов со следующими логическими именами:

stdin - для ввода данных из стандартного входного потока (по умолчанию - c клавиатуры);
stdout - для вывода данных в стандартный выходной поток (по умолчанию - на экран дисплея);
stderr - файл для вывода сообщений об ошибках (всегда связан с экраном дисплея);
stdprn - для вывода данных на принтер;
stdaus - для ввода и вывода данных в коммуникационный канал.

В языке Си имеется также система низкоуровневого ввода/вывода (без буферизации и форматирования данных), соответствующая стандарту системы UNIX. Прототипы составляющих ее функций находятся в файле io.h. К этим функциям относятся:

open() - открыть файл;
close() - закрыть файл;
read() - читать данные;
write() - записать данные;
lseek() - поиск определенного байта в файле;
unlink() - уничтожить файл.

Лекция 6. Препроцессор языка Си, С++

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

Директива

#define идентификатор подстановка

вызывает замену в последующем тексте программы названного идентификатора на текст подстановки (обратите внимание на отсутствие точки с запятой в конце этой команды). По существу, эта директива вводит макроопределение (макрос), где "идентификатор" - это имя макроопределения, а "подстановка" - последовательность символов, на которые препроцессор заменяет указанное имя, когда находит его в тексте программы. Имя макроопределения принято набирать прописными буквами.

Рассмотрим примеры:

#define MAX 25

#define BEGIN {

Первая строка вызывает замену в программе идентификатора MAX на константу 25. Вторая позволяет использовать в тексте вместо открывающей фигурной скобки ({) слово BEGIN.

Отметим, что поскольку препроцессор не проверяет совместимость между символическими именами макроопределений и контекстом, в котором они используются, то рекомендуется такого рода идентификаторы определять не директивой #define, а с помощью ключевого слова const с явным указанием типа (это в большей степени относится к Си++):

const int MAX = 25;

(тип int можно не указывать, так как он устанавливается по умолчанию).

Если директива #define имеет вид:

#define идентификатор(идентификатор,..., идентификатор) подстановка

причем между первым идентификатором и открывающей круглой скобкой нет пробела, то это определение макроподстановки с аргументами. Например, после появления строки вида:

#define READ(val) scanf("%d", &val)

оператор READ(y); воспринимается так же, как scanf("%d",&y);. Здесь val - аргумент и выполнена макроподстановка с аргументом.

При наличии длинных определений в подстановке, продолжающихся в следующей строке, в конце очередной строки с продолжением ставится символ \.

В макроопределение можно помещать объекты, разделенные знаками ##, например:

#define PR(x, у) x##y

После этого PR(а, 3) вызовет подстановку а3. Или, например, макроопределение

#define z(a, b, c, d) a(b##c##d)

приведет к замене z(sin, x, +, y) на sin(x+y).

Символ #, помещаемый перед макроаргументом, указывает на преобразование его в строку. Например, после директивы

#define PRIM(var) printf(#var"= %d", var)

следующий фрагмент текста программы

year = 2006;

PRIM(year);

преобразуется так:

year = 2006;

printf("year""= %d", year);

Опишем другие директивы препроцессора. Директива #include уже встречалась ранее. Ее можно использовать в двух формах:

#include "имя файла"

#include <имя файла>

Действие обеих команд сводится к включению в программу файлов с указанным именем. Первая из них загружает файл из текущего или заданного в качестве префикса каталога. Вторая команда осуществляет поиск файла в стандартных местах, определенных в системе программирования. Если файл, имя которого записано в двойных кавычках, не найден в указанном каталоге, то поиск будет продолжен в подкаталогах, заданных для команды #include <...>. Директивы #include могут вкладываться одна в другую.

Следующая группа директив позволяет избирательно компилировать части программы. Этот процесс называется условной компиляцией. В эту группу входят директивы #if, #else, #elif, #endif, #ifdef, #ifndef. Основная форма записи директивы #if имеет вид:

#if константное_выражение последовательность_операторов

#endif

Здесь проверяется значение константного выражения. Если оно истинно, то выполняется заданная последовательность операторов, а если ложно, то эта последовательность операторов пропускается.

Действие директивы #else подобно действию команды else в языке Си, например:

#if константное_выражение

последовательность_операторов_1

#else

последовательность_операторов_2

#endif

Здесь если константное выражение истинно, то выполняется последовательность_операторов_1, а если ложно - последовательность_операторов_2.

Директива #elif означает действие типа "else if". Основная форма ее использования имеет вид:

#if константное_выражение

последовательность_операторов

#elif константное_выражение_1

последовательность_операторов_1

#elif константное_выражение_n

последовательность_операторов_n

#endif

Эта форма подобна конструкции языка Си вида: if...else if...else if...

Директива

#ifdef идентификатор

устанавливает определен ли в данный момент указанный идентификатор, т.е. входил ли он в директивы вида #define. Строка вида

#ifndef идентификатор

проверяет является ли неопределенным в данный момент указанный идентификатор. За любой из этих директив может следовать произвольное число строк текста, возможно, содержащих инструкцию #else (#elif использовать нельзя) и заканчивающихся строкой #endif. Если проверяемое условие истинно, то игнорируются все строки между #else и #endif, а если ложно, то строки между проверкой и #else (если слова #else нет, то #endif). Директивы #if и #ifndef могут "вкладываться" одна в другую.

Директива вида

#undef идентификатор

приводит к тому, что указанный идентификатор начинает считаться неопределенным, т.е. не подлежащим замене.

Рассмотрим примеры. Три следующие директивы:

#ifdef WRITE

#undef WRITE

#endif

проверяют определен ли идентификатор WRITE (т.е. была ли команда вида #define WRITE...), и если это так, то имя WRITE начинает считаться неопределенным, т.е. не подлежащим замене.

Директивы

#ifndef WRITE

#define WRITE fprintf

#endif

проверяют является ли идентификатор WRITE неопределенным, и если это так, то определятся идентификатор WRITE вместо имени fprintf.

Директива #error записывается в следующей форме:

#error сообщение_об_ошибке

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

Директива #line предназначена для изменения значений переменных _LINE_ и _FILE_, определенных в системе программирования Си. Переменная _LINE_ содержит номер строки программы, выполняемой в текущий момент времени. Идентификатор _FILE_ является указателем на строку с именем компилируемой программы. Директива #line записывается следующим образом:

#line номер "имя_файла"

Здесь номер - это любое положительное целое число, которое будет назначено переменной _LINE_, имя_файла - это необязательный параметр, который переопределяет значение _FILE_.

Директива #pragma позволяет передать компилятору некоторые указания. Например, строка

#pragma inline

говорит о том, что в программе на языке Си имеются строки на языке ассемблера. Например:

asm mov ax, 5

asm {

inc dx

sub bl, al

}

и т.д.

Рассмотрим некоторые глобальные идентификаторы или макроимена (имена макроопределений). Определены пять таких имен: _LINE_, _FILE_, _DATE_, _TIME_, _STDC_. Два из них (_LINE_ и _FILE_) уже описывались выше. Идентификатор _DATE_ определяет строку, в которой сохраняется дата трансляции исходного файла в объектный код. Идентификатор _TIME_ задает строку, сохраняющую время трансляции исходного файла в объектный код. Макрос _STDC_ имеет значение 1, если используются стандартно - определенные макроимена. В противном случае эта переменная не будет определена.

Рассмотрим примеры программ, в которых используются различные конструкции языка Си. Первый из них демонстрирует использование управляющих символов n в функциях printf() и scanf().

/* Пример 1 */

#include <stdio.h>

 

void main(void)

{

int x, n1, n2;

 

printf("Введите целое число от -32768 до 32767\n");

scanf("%d%n", &x, &n1);

printf("x = %d%n\n", x, &n2);

printf("n1 = %d, n2 = %d\n", n1, n2);

}

Результаты работы этой программы имеют вид:

Введите целое число от -32768 до 32767

234<Enter>

x = 234

n1 = 3, n2 = 7

Значение n1 определяет число введенных цифр, а n2 - число выведенных символов в строке x = 234 (с пробелами).

Следующий пример показывает использование спецификаций %[], а также символов * и #.

/* Пример 2 */

#include <stdio.h>

 

void main(void)

{

char str_b[21], str_c[21]; /* Последний элемент резервируем под \0 */

int x, n1, n2;

float y;

 

printf("Введите строку до 20 символов\n");

scanf("%[Computer]%s", str_b, str_c);

printf("str_b = %s, str_c = %s\n", str_b, str_c);

y = 12.345678;

n1 = 8;

n2 = 3;

x = 0x100;

printf("y = %*.*f\n", n1, n2, y);

printf("x(16) = %#x, x(16) = %x, x(10) = %i\n", x, x, x);

}

Результаты работы программы имеют следующий вид:

Введите строку до 20 символов

Comp-1-2-3-4-5<Enter>

str_b = Comp, str_c = -1-2-3-4-5

у = 12.346

x(16) = 0x100, x(16) = 100, x(10) = 256

Здесь пользователем введена строка Comp-1-2-3-4-5. Из нее только четыре символа (Comp) совпадают с первыми символами, заданными в квадратных скобках рассматриваемой спецификации [Computer]. Поэтому только эти четыре символа попадут в первую строку, а оставшиеся символы (1-2-3-4-5) попадут во вторую строку. Число n1 определяет минимальную ширину поля для вывода, а число n2 = 3 - количество цифр после запятой. В результате число 12.345678 будет смещено относительно левой границы, и после запятой будут выведены три цифры. Шестнадцатеричное число 0х100 выведено функцией printf() с префиксом 0x, без префикса 0x и в десятичной форме.

Третья программа демонстрирует использование условного оператора if...else и оператора for для организации цикла.

/* Пример 3 */

#include <conio.h>

#define SYM 'X' /* Выводимый символ */

#define SPACE ' ' /* Определение пробела */

#define LF 10 /* Перевод строки */

#define CR 13 /* Возврат каретки */

#define LEFT 24 /* Левая граница символа */

#define RIGHT 51 /* Правая граница символа */

#define BOTTOM 25 /* Нижняя граница символа */

 

void main(void)

{

int col, line; /* col - номер колонки для вывода символа */

/* line - номер линям для вывода символа */

clrscr();

for (line = 1; line <= BOTTOM; line++) /* Вывод пробелов до левой

границы символа */

{

for (col = 1; col < LEFT; col++) putch(SPACE);

for(col = LEFT + 1; col < RIGHT; col++) /* Вывод символа X

на весь экран */

if ((col == (LEFT + line)) || (col == (RIGHT - line)))

putch(SYM);

else putch(SPACE);

putch(LF); /* Возврат каретки и перевод строки после */

putch(CR); /* вывода каждой линии символа */

}

getch(); /* Ожидание нажатия клавиши */

}

После ее запуска на весь экран будет выведен символ X.

Новая библиотечная функция clrscr() имеет следующий прототип:

void clrscr (void);

Она выполняет очистку экрана и объявлена в заголовочном файле conio.h.

Четвертая программа демонстрирует использование рекурсивной функции для вычисления факториала. (Отметим, что определение функции factorial() может находиться и после функции main(), но в этом случае функция factorial() должна быть объявлена перед функцией main(), т.е. до main() необходимо поместить строку: long factorial(int);.)

/* Пример 4 */

#include <stdio.h>

#include <values.h>

#include <process.h>

 

long factorial(int value) /* Рекурсивная функция */

{

long result = 1;

 

if (value!= 0)

{

result = factorial(value - 1);

/* Проверка возможности вычисления факториала */

if (result > MAXLONG / (value + 1))

{

fprintf(stderr, "Очень большое число\n");

getch(); /* Ожидание нажатия клавиши */

exit (1);

}

result *= value;

}

return(result);

}

/* Рекурсивное вычисление факториала числа value */

void main(void)

{

int value; /* Факториал этого значения вычисляется */

long result; /* Переменная для результата */

 

puts("Факториал какого числа?");

scanf("%d", &value);

result = factorial(value);

printf("Результат: %ld\n", result);

getch(); /* Ожидание нажатия клавиши */

}

Результаты работы этой программы:

Факториал какого числа? 10<Enter>

Результат: 362880

Пятая программа подсчитывает число символов и слов во вводимых строках (новые символы и слова суммируются с предыдущими; пробелы входят в число введенных символов).

/* Пример 5 */

#include <stdio.h>

#include <conio.h>

#define ESC 27 /* 27 - ASCII-код клавиши ESC */

 

void CountOfLines(void)

{

/* Статические переменные будут сохранять старые значения при каждом

новом вызове функции CountOfLines */

static int words = 0, symbols = 0; /* words-число слов,

symbols-число символов */

char temp, t = 0; /* Временные переменные */

 

++symbols;

/* Число символов и слов выдается после нажатия клавиши <Enter> */

while ((temp = getche())!= '\r')

{

++symbols; /* Подсчитывается каждый символ */

/* После одного или нескольких пробелов подсчитывается слово */

if ((temp == ' ') && (t == 1)) continue;

if (temp == ' ') { t = 1; ++words; }

else t = 0;

}

if (t == 1) --words;

else ++words;

printf ("\n Слов: %d; символов: %d\n", words, symbols);

}

void main(void)

{

puts("Для завершения программы нажмите <ESC> в начале строки");

puts("Строка не должна начинаться с пробела и с нажатия клавиши"

"<Enter>");

puts("Строка не должна завершаться пробелом");

while (getche()!= ESC) CountOfLines();

putch('\b');

putch(' ');

putch('\b');

}

Результаты работы этой программы:

Для завершения программы нажмите <ESC> в начале строки

Строка не должна начинаться с пробела и с нажатия клавиши <Enter>

Строка не должна завершаться пробелом

Mouse Keyboard <Enter>

Слов: 2 символов: 14

<ESC>

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

· занесение фамилии абонента и номера телефона в справочник;

· поиск в справочнике номера телефона по фамилии абонента;

· удаление из справочника фамилии абонента и номера его телефона.

Ниже приведен текст головной программы main.c:

// Пример 6

//---------------------------------------------------------

// Головная программа для работы с телефонным справочником

//---------------------------------------------------------

#include "A:\my.h" //Заголовочный файл с глобальными

//переменными и константами

#include "A:\findt.c" //Поиск строки str в файле

#include "A:\choicet.c" //Проверка наличия строки в файле

#include "A:\addt.c" //Добавление строки в файл

#include "A:\subt.c" //Удаление строки из файла

 

void main(int argc, char *argv[ ])

{

if (argc == 3)

if (*argv[1] == '+') //Добавить запись

{

if (Choice(argv[2]) == 0) //Нет ли такой

//записи в файле?

{

puts("Эта фамилия есть в справочнике");

exit(1);

}

Add(argv[2]); //Добавление записи

}

else if (*argv[1] == '-') Sub(argv[2]); //Удалить запись

else puts("Ошибочное значение аргумента");

else if (argc == 2) Find(argv[1]); //Поиск записи

else puts("Ошибочное число аргументов");

}

С помощью директив #include в головную программу включаются файлы: my.h, findt.c, choicet.c, addt.c и subt.c. Считается, что все они находятся в корневом каталоге диска A:. Если это не так, то необходимо изменить соответствующие директивы #include. В файле my.h определены глобальные переменные и некоторые символьные значения.

//Файл заголовков my.h

//--------------------------------------------------------

//Определения глобальных переменных и символьных значений

//--------------------------------------------------------

#include <stdio.h>

#include <process.h>

#include <string.h>

#define MAX_NAME 20 //Максимальное число символов в фамилии

#define MAX_NUMBER 10 //Максимальное число цифр в телеф. номере

char Name[MAX_NAME]; //Массив фамилий

char Number[MAX_NUMBER]; //Массив для телефонного номера

char File[ ] = "A:\\tel\\tel_num.txt"; //Имя файла справочника

int Count; //Число фамилий в справочнике

FILE *F_tel; //Логическое имя файла справочника

Файл my.h, в частности, определяет, что телефонный справочник будет организован в каталоге tel диска A:. Поэтому необходимо перед запуском программы main.exe создать этот подкаталог либо использовать другой подкаталог. В последнем случае необходимо изменить строку:

char File[ ] = "A:\\tel\\tel_num.txt";

которая задает имя файла с телефонным справочником (tel_num.txt).

Модуль findt.c, текст которого приведен ниже, содержит функцию Find() для поиска строки str в файле tel_num.txt.

//Модуль findt.c

//----------------------------------------------------------

//Функция Find() для поиска строки str в файле tel_num.txt

//----------------------------------------------------------

void Find(char *str)

{

int i;

//Если файл невозможно открыть для чтения, то программа

//завершает работу

if ((F_tel = fopen(File, "r")) == NULL)

{

fprintf(stderr, "\"%s\": невозможно открыть\n", File);

exit(1);

}

//Чтение числа записей (Count) в файле

if (fread(&Count, sizeof(int), 1, F_tel)!= 1)

{

fprintf(stderr, "\"%s\": ошибка чтения\n", File);

exit(1);

}

//В цикле for осуществляется поиск нужной записи

for (i = 1; i < Count; i++)

{

fread(Name, 1, MAX_NAME, F_tel); //Чтение имени

fread(Number, 1, MAX_NUMBER, F_tel); //Чтение номера

if (ferror(F_tel)) //Проверка отсутствия ошибки

{

fprintf(stderr, "\"%s\": ошибка чтения\n'', File);

exit(1);

}

if (strcmp(str, Name) == 0) //Если имя совпадает

//с введенным, то фамилия

//и найденный номер

//выводятся на экран

{

printf("Фамилия: %s\n", Name);

printf("Номер телефона: %s\n", Number);

fclose(F_tel);

return;

}

}

//Если результат поиска отрицательный, то выводится

//следующее сообщение

fprintf(stderr,"\"%s\": запись в файле отсутствует\n", File);

fclose(F_tel);

return;

}

Модуль choicet.c содержит функцию Choice(), позволяющую проверить есть ли заданная строка в файле tel_num.txt.

 

// Модуль choicet.c

//----------------------------------------------------------------------

//Функция Choice(), проверяющая есть ли строка str в файле tel_num.txt

//----------------------------------------------------------------------

int Choice(char *str)

{

int i;

char temp[MAX_NAME + MAX_NUMBER];

 

if ((F_tel = fopen(File, "r")) == NULL)

return 1; //Строки str нет в файле

if (fread(&Count, sizeof(int), 1, F_tel)!= 1)

{

fprintf(stderr, "\"%s\": ошибка чтения\n", File);

exit(1);

}

for (i = 0; i < Count; i++)

{

fread(temp, 1, MAX_NAME + MAX_NUMBER, F_tel);

if (ferror(F_tel))

{

fprintf(stderr, "\"%s\": ошибка чтения\n", File);

exit(1);

}

if (strcmp(str, temp) == 0)

{

fclose(F_tel);

return 0; //Строка str есть в файле

}

}

fclose(F_tel);

return 1; //Строки str нет в файле

}

Модуль addt.c содержит функцию Add(), которая добавляет заданную строку в файл tel_num.txt.

//Модуль addt.c

//-------------------------------------------------------

//Функция Add() добавляет строку str в файл tel_num.txt

//-------------------------------------------------------

void Create(void) //Создает файл, если он не существует

{

if ((F_tel = fopen(File, "wb+")) == NULL)

{

fprintf(stderr, "\%s\": невозможно открыть\n", File);

exit(1);

}

Count = 0;

if (! fwrite(&Count, sizeof(Count), 1, F_tel))

{

fprintf(stderr, "\"%s\": ошибка записи\n", File);

exit(1);

}

}

void Add(char *s) //Добавляет запись в файл

{

char str[MAX_NAME], sn[MAX_NUMBER]; //Временные массивы

int i;

 

for (i = 0; i < MAX_NAME; i++)

str[i] = ' '; //Пробелы в str

strcpy(str, s); //Копирование строки в str

if ((F_tel = fopen(File, "rb+")) = = NULL)

Create(); //Создаем файл, если он не

//существует

else if (fread(&Count, sizeof(Count), 1, F_tel)!= 1)

{

fprintf(stderr, "\"%s\": ошибка чтения\n", File);

exit(1);

}

printf("Номер телефона: "); //Запрашивается и вводится номер

if (gets(Number) == NULL || *Number == '\0')

{

fclose(F_tel);

return; //Возврат, если номер не введен

}

//Установка указателя в файле на первую свободную запись

if (fseek(F_tel, (long)((MAX_NAME+MAX_NUMBER)*Count), SEEK_CUR)!=0)

{

fprintf(stderr, "\"%s\": ошибка поиска\n", File);

exit(1);

}

fwrite(str, 1, MAX_NAME, F_tel); //Запись в файл фамилии

for (i = 0; i < MAX_NUMBER; i++)

sn[i] = ' '; //Пробелы в sn[ ]

strcpy(sn, Number); //Копирование сроки Number в строку sn

fwrite(sn, 1, MAX_NUMBER, F_tel); //Запись в файл номера

if (ferror(F_tel)) //Проверка наличия ошибки

{

fprintf(stderr, "\"%s\": ошибка записи\n", File);

exit(1);

}

//Установка указателя в файле на первый байт

if (fseek(F_tel, 0L, SEEK_SET)!= 0)

{

fprintf(stderr, "\"%s\": ошибка позиционирования\n", File);

exit(1);

}

++Count; //Увеличение числа записей на единицу

//Запись Count в файл

if (fwrite(&Count, sizeof(int), 1,F_tel)!= 1)

{

fprintf(stderr, "\"%s\": ошибка записи\n", File);

exit(1);

}

fclose(F_tel);

return;

}

Модуль subt.c содержит функцию Sub(), которая удаляет заданную строку из файла tel_num.txt.

//Модуль subt.c

//------------------------------------------------------------

//Функция Sub() удаляет заданную строку из файла tel_num.txt

//------------------------------------------------------------

void Sub(char *str)

{

int i, j;

char temp[MAX_NAME + MAX_NUMBER]; //Временный массив

 

if ((F_tel = fopen(File, "r+")) == NULL)

{

fprintf(stderr, "\"%s\": невозможно открыть\n", File);

exit(1);

}

if (fread(&Count, sizeof(int), 1, F_tel)!= 1)

{

fprintf(stderr, "\"%s\": ошибка чтения\n", File);

exit(1);

}

//В цикле осуществляется поиск удаляемой строки в файле

for (i = 0; i < Count; i++)

{

fread(temp, 1, MAX_NAME + MAX_NUMBER, F_tel);

if (ferror(F_tel))

{

fprintf(stderr, "\"%s\": ошибка чтения\n", File);

exit(1);

}

if (strcmp(str, temp) == 0) //Если срока найдена

{

for (j = i; j < Count; j++) //она удаляется

{

fread(temp, 1, MAX_NAME + MAX_NUMBER, F_tel);

fseek(F_tel, (long)(j*(MAX_NAME+MAX_NUMBER)+2L), SEEK_SET);

fwrite(temp, 1, MAX_NAME + MAX_NUMBER, F_tel);

fseek(F_tel, (long)((j+2)*(MAX_NAME+MAX_NUMBER)+2L), SEEK_SET);

if (ferror(F_tel))

{

fprintf(stderr, "\"%s\": ошибка чтения\n", File);

exit(1);

}

}

--Count; //При удалении строки декремент Count

fseek(F_tel, 0L, SEEK_SET); //Установка указателя

//Запись уменьшенного значения Count в файл

if (fwrite(&Count, sizeof(Count), 1, F_tel)!= 1)

{

fprintf(stderr, "\"%s\": ошибка записи\n", File);

exit(1);

}

fclose(F_tel);

puts("Запись удалена из файла");

return;

}

}

fprintf(stderr, "\"%s\": отсутствует в базе данных\n", File);

fclose(F_tel);

}

Ниже приводится возможный сценарий работы с программой main.

main + Петров<Enter>

Номер телефона: 77-17-89<Enter>

main + Иванов<Enter>

Номер телефона: 52-98-02<Enter>

 

main Иванов<Enter>

Фамилия: Иванов

Номер телефона: 52-98-02

 

main - Петров<Enter>

Запись удалена из файла

 

main Петров<Enter>

"tel_num.txt": запись в файле отсутствует

Последняя программа showt.c позволяет вывести на экран содержимое телефонного справочника.

//Программа showt.c

//-------------------------------------------------

//Выводит на экран все записи из файла tel_num.txt

//-------------------------------------------------

#include "my.h"

void Show(void)

{

int i;

//Если файл невозможно открыть для чтения, то завершение работы программы

if ((F_tel = fopen(File, "r")) == NULL)

{

fprintf(stderr, "\"%s\": невозможно открыть\n",File);

exit(1);

}

//Чтение числа записей (Count) в файле

if(fread(&Count, sizeof(int), 1, F_tel)!= 1)

{

fprintf(stderr, "\"%s\": ошибка чтения\n", File);

exit(1);

}

//В цикле осуществляется вывод всех записей

for (i=0; i < Count; i++)

{

fread(Name, 1, MAX_NAME, F_tel); //Читается имя

fread(Number, 1, MAX_NUMBER, F_tel); //Читается номер

if (ferror(F_tel)) //Проверяется отсутствие ошибки

{

fprintf(stderr, "\"%s\": ошибка чтения\n'', File);

exit(1);

}

printf("Фамилия: %s; номер телефона: %s\n", Name, Number);

}

fclose(F_tel);

}

void main(void)

{

Show();

}

 

<== предыдущая лекция | следующая лекция ==>
Лекция 5. Функции. Рекурсивные функции. Файлы. Функции для работы с файлами | Конспект ЛЕКЦИЙ
Поделиться с друзьями:


Дата добавления: 2014-01-04; Просмотров: 301; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.56 сек.