Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Text_lektsy_Si-dlya_studentov.pdf
Скачиваний:
6
Добавлен:
08.05.2015
Размер:
1.22 Mб
Скачать

Заключительный пример

#include <stdio.h> #define SIZE 4

ВЫЧИСЛЕНИЕ КВАДРАТИЧНОЙ ФОРМЫ

void main(void)

{

float u[SIZE],

/* Входной вектор */

b[SIZE][SIZE],

/* Входная матрица */

v[SIZE],

/* Вектор b*u */

z,

/* Результат */

scalar();

/* Скалярное произведение векторов */

int i, j;

 

 

printf ( "Исходный вектор: \n" ); for ( i=0; i<SIZE; i++ ) { scanf ( "%f", &u[i] );}

printf ( "Исходная матрица: \n" ); for ( i=0; i<SIZE; i++ ) {

for ( j=0; j<SIZE; j++ ) { scanf ( "%f", &b[i][j] ); } } matrix(b, u, v); z=scalar(v, u);

printf ( " \n \n \n Квадратичная форма равна %.5g \n", z);

}/* End main */

/* Умножение матрицы на вектор */

void matrix ( float a[][SIZE], float x[], float y[] ) { int i, j;

for ( i=0; i<SIZE; i++ ) { for ( y[i]=j=0; j<SIZE; j++ ) { y[i] += a[i][j]*x[j];

}

}

}/* End matrix */

/* Скалярное произведение векторов */

float scalar (float x[], float y[] )

{

int i;

 

 

for ( z = i = 0; i<SIZE; i++ ) {

 

z += x[i]*y[i];

 

}

 

 

return z;

 

}

/* End scalar */

 

51

Глава 9 СИМВОЛЬНАЯ ИНФОРМАЦИЯ И СТРОКИ

Для представления текстовой информации в языке СИ используются символы (константы), символьные переменные и строки (строковые константы), для которых в языке СИ не введено отдельного типа в отличие от некоторых других языков программирования.

Символьные константы используются для изображения отдельных знаков, имеющих индивидуальные внутренние коды. Каждая символьная константа – это лексема (элемент языка СИ), которая состоит из изображения символа и ограничивающих апост-

рофов: ‘A’, ‘a’,’B’,’8’,’+’,’;’.

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

Для символьных данных (переменных) введен базовый тип char. Описание символьных переменных имеет вид:

char список_имен_переменных;

Пример: char a, z;

9.1Ввод - вывод символьных данных

Для ввода и вывода символьных значений в форматных строках библиотечных функций printf( ) и scanf( ) используется спецификация преобразования % c.

Пример:

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

/* удаление повторяющихся пробелов */

#include<stdio.h> void main()

{

char z,s; /*z–текущий вводимый символ*/ /*s–предыдущий введенный символ*/

printf(“\nНапишите предложение с точкой в конце: \n”); for ( z=s=’ ’;z!=’.’;s=z ) {

scanf(“%c”,&z); // z=getch(); if ( z ! =’ ‘ || s !=’ ‘)

printf(“%c”,z); } /*конец цикла обработки*/

}/*конец программы*/

В программе две символьные переменные: z – для чтения очередного символа и s – для хранения предыдущего. В заголовке цикла переменные s и z получают значение “пробел”. Очередной символ вводится как значение переменной z, и пара z, s анализируется. Если хотя бы один из символов значений пары отличен от пробела, то значение z печатается. В заголовке цикла z сравнивается с символом “точка” и при несовпадении запоминается как значение s. Далее цикл повторяется до появления на входе точки, причем появление двух пробелов (z и s)приводит к пропуску оператора печати.

52

9.2Внутренние коды и упорядоченность символов

ВАЖНО ! В языке принято соглашение, что везде, где синтаксис позволяет

использовать целые числа, можно использовать и символы, т.е. данные типа char, ко-

торые при этом представляются числовыми значениями своих внутренних кодов.

Такое соглашение позволяет сравнительно просто упорядочивать символы, обращаясь с ними как с целочисленными величинами.

Например, внутренние коды десятичных цифр в таблицах кодов ASCII упорядочены по числовым значениям, поэтому несложно перебирать символы десятичных цифр в нужном порядке.

Пример:

Следующая программа печатает цифры от 0 до 9 и шестнадцатеричные представления их внутренних кодов.

/*печать десятичных цифр*/

#include<stdio.h> void main()

{

char z;

 

 

for ( z=’0’, z<=’9’, z++ ) {

printf(“\n”);

 

if ( z= =’0’ | |z= =’5’)

 

printf( “%c – %x”, z, z);

 

}

}

 

/*конец программы*/

 

Результат выполнения программы: 0–301–312–323–334–34 5–356–367–378–389–39

Обратите внимание на то, что символьная переменная z является операндом арифметической операции ‘++’, выполняемой над числовым представлением ее внутреннего кода. Для изображения значения символов в форматной строке функции printf( ) используется спецификация . шестнадцатеричные коды выводятся с помощью спецификации .

Пример:

Воспользовавшись упорядоченностью внутренних кодов букв латинского алфавита, очень просто его напечатать.

/*печать латинского алфавита*/

#include<stdio.h> void main() {

char z;

for ( z=’A’, z<=’Z’, z++) printf(“%c”,z);

}/*конец программы*/

Результат выполнения программы: ABCDEFGHIJKLMNOPQRSTUVWXYZ

53

9.3Строки или строковые константы

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

Примеры:

“1234567890”

“\t состав президиума”

“начало строки \t и конец строки”.

В качестве терминирующего символа выбран символ с кодом 0 (не путайте его с

символом '0'). Таким образом, определение

char HelloStr [] = "Hello, world";

фактически интерпретируется как

char HelloStr [] = {'H', 'e', 'l', 'l', 'o', ' ', ',', 'w', 'o', 'r', 'l', 'd', '\0'};

Это происходит при инициализации массива строкой. Во всех остальных случаях встретившаяся строка интерпретируется как еще не созданный безымянный массив соот-

ветствующей длины. То есть выражение printf ("Hello, world\n");

на самом деле интерпретируется как

char str1 [ ] = "Hello, world\n"; printf (str1);

При размещении строки в памяти транслятор автоматически добавляет в ее конец символ ‘\0’, т.е. нулевой байт.

Количество элементов в таком массиве на 1 больше, чем в изображении соответствующей строковой константы, т.к. в конец строки добавили нулевой байт ‘\0’.

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

Присвоить значение массиву символов (т.е. строке) с помощью обычного опера-

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

разования %s.

Пример:

/*печать символьной строки*/

#include<stdio.h> void main()

{char B[]=”Cезам, откройся!”; printf(“%s”,B); } /*конец программы*/

Результат выполнения программы: Сезам, откройся!

В программе длина массива В – 17 элементов, т.е. длина строки, помещаемой в массив (16 символов), плюс нулевой байт окончания строки. Именно 17 байтов выделя-

54

ется при инициализации массива в приведенном примере. Инициализация массива символов с помощью строковой константы представляет собой сокращенный вариант инициализации массива и введена в язык для упрощения. Можно воспользоваться обычной инициализацией, поместив начальные значения элементов массива в фигурные скобки и не забыв при этом поместить в конце списка начальных значений специальный символ окончания строки ‘\0’. Таким образом, в программе была допустима такая инициализа-

ция массива В:

char B[ ] = {‘C’,’е’,’з’,’а’,’м’,’,’,’ ’,’о’,’т’,’к’,’р’,’о’,’й’,’с’,’я’,’!’,’\0’};

Как вводить строки или выводить их на экран?

Выводиться строка может или уже известной вам функцией printf( ) со спецификатором ввода "%s", либо специальной функцией int puts (char *string), ко-

торая выводит строку string на экран и возвращает некоторое ненулевое значение в случае успеха.

Зачем нужен спецификатор "%s"? Это делается для того, чтобы можно было выводить строки с любыми символами. Сравните:

Пример:

char str [] = "Захотелось мне вывести %d..."; printf ("%s", str); /* Правильный вариант */

printf

("\n");

/* Разделитель

новой строки */

printf

(str);

/* Неправильный

вариант */

В первом случае функция напечатает именно то, что от нее требуется. А вот во втором случае printf(), встретив в строке str спецификатор "%d" (ведь теперь эта строка – первая, значит, она задает формат вывода), сделает вывод, что за ней должно следовать число. А так как оно не следует, то вместо "%d" будет напечатан некоторый мусор – число, находящееся в тот момент в стеке.

Последствия могут быть и более серьезными, если в строке находится последовательность "%s", то сочтет ее за строку, и будет выводить ее до тех пор, пока не встретит нулевой символ. А где она его встретит, сколько успеет напечатать и не crash'нется ли изза обращения не к своей памяти – не знает никто.

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

Пример: Необходимо сохранить введенную строку.

/*чтение строки с терминала*/

int getline(char s, /*введенная строка*/ int lim) /*ее максимальная длина*/

{ int c, i; /* с – вводимый символ*/

for (

i=0; i<lim && (c = getchar() )!=EOF && c!=’\n’;

i++ ) {

s[i]=c;

}

 

s[i] = ’\0’;

}

return

i;

9.5Ввод–вывод строк. Основные функции работы со строками

55

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

Для ввода строки существует функция с прототипом char *gets (char *string),

которая считывает строку с клавиатуры и помещает ее в буфер string, указатель на который и возвращает. Если произошла ошибка, то возвращается EOF.

Функция имеет только один аргумент – указатель s на массив символов. Прототип функции описан в файле <stdio.h>.

Функция gets() завершает свою работу при вводе символа ‘\n’, который автоматически передается с клавиатуры в ЭВМ при нажатии клавиши <Enter>. При этом сам символ ‘\n’ во вводимую строку не записывается. Вместо него в строку помещается нуль–символ ‘\0’. Таким образом, функция gets() производит ввод “правильной” строки,

а не просто последовательности символов.

Здесь следует обратить внимание на следующую особенность ввода данных с клавиатуры. Функция gets() начинает обработку информации от клавиатуры только после нажатия клавиши <Enter>. Таким образом, она “ожидает”, пока не будет набрана нужная информация и нажата клавиша <Enter>. Только после этого начинается ввод

данных в программу.

Пример: #include <stdio.h> int main (void) {

char string[80];

printf ( "Input a string:" ); gets ( string );

printf ( "The string input was: %s\n", string ); return 0; }

Функция puts() (вывод строки на экран) в случае успешного завершения возвращает последний выведенный символ, который всегда является символом ‘\n', если произошла ошибка, то возвращается из функции EOF. Прототип этой функции имеет следующий вид:

int puts ( char*s ); /*функция вывода строки*/

Функция имеет только один аргумент – указатель s на массив символов. Прототип функции описан в файле <stdio.h>.

Приведем простейший пример использования этих функций.

#include<stdio.h>

char strl[ ] = ”введите фамилию сотрудника:”; int main ( ) {

char name[80]; puts(strl0; gets(name); return 0; }

Напомним, что любая строка символов в языке СИ должна заканчиваться нуль– символом ‘\0’. В последний элемент массива strl нуль–символ будет записан автомати-

56

чески во время трансляции при инициализации массива. Для функции puts() наличие нуль-символа в конце строки является обязательным.

В противном случае, т.е. при отсутствии в символьном массиве символа ‘\0’,

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

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

Пример: #include <stdio.h> int main ( void ) {

char string[] = "This is an example output string\n"; puts(string);

return 0;}

9.5Функции манипуляции со строками

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

Для манипуляций со строками существует набор функций, объявленных в файле <string.h> (те, кто пишет под Windows, могут включать вместо него файл <windows.h>).

Наиболее важные функции:

I. int strcmp (char *string1, char *string2)

осуществляет сравнение двух строк. Возвращает отрицательное число, если первая строка меньше второй, 0, если строки равны и положительное число, если первая строка больше второй. Более детально, функция возвращает разницу между кодами первых встретившихся неодинаковых символов (если строки неодинаковы по длине, то когда-то ненулевой символ будет сравниваться с нулем).

Пример:

#include <string.h>

#include <stdio.h>

 

 

int main (void) {

*buf2 = "bbb",

*buf3 = "ccc";

char

*buf1 = "aaa",

int

ptr;

buf1);

 

ptr = strcmp (buf2,

 

if

(ptr > 0)

 

 

printf("buffer 2 is greater than buffer 1 \n" );

else

 

 

 

printf("buffer 2 is less than buffer 1 \n");

ptr = strcmp( buf2,

buf3 );

 

if (ptr > 0)

 

buffer 3\n");

printf("buffer 2 is greater than

else

printf("buffer 2 is less than buffer 3\n" ); return 0; }

На экране появится:

57

buffer 2 is greater than buffer 1 buffer 2 is less than buffer 3

II. char *strcpy (char *dest, char *source)

осуществляет копирование строки source на место строки dest. Опять-таки позаботьтесь о том, чтобы вся строка поместилась в отведенном для нее месте. Функция возвращает указатель на строку-приемник.

Пример: #include <stdio.h>

#include <string.h> int main (void) { char string[10];

char *str1 = "a b c d e f g h i"; strcpy (string, str1);

printf( "%s \n", string); return 0; }

На экране появится:

a b c d e f g h I

III.char *strcat (char *string1, char *string2)

осуществляет склеивание двух строк. Вторая строка добавляется в конец первой. Функция не проверяет (да и не может проверять технически) наличие необходимого количества памяти в конце первой строки – об этом должны позаботиться вы. Функция

возвращает указатель на первую строку.

 

Пример:

 

 

 

#include <string.h>

 

 

#include <stdio.h>

{

 

int main(void)

 

char

destination[25];

 

char

*blank = " ",

*c = "C++", *turbo = "Turbo";

strcpy (destination, turbo);

//Копирование строки "turbo"

на место destination

 

// Склеивание destination …

strcat (destination, blank);

strcat (destination, c);

// сначала с blank, потом с c

printf("%s\n", destination);

 

return 0; }

 

 

 

На экране появится:

Turbo C ++

 

IV. int strlen

(char *string)

возвращает длину строки string (не считая нулевого символа).

V. char *strdup (char *string)

создает дубликат строки string и возвращает указатель на него. Учтите, что в отличие от остальных функций, strdup сама создает строку и поэтому после того, как она стала вам не нужна, не забывайте ее освободить.

VI. char *strncpy ( char *dest, char *source, int count) VII. char *strncat ( char *string1, char *string2, int

count)

58

аналогично strcpy и strcat, но копируются только первые count символов. Функции не добавляют к строке завершающего нуля – вам придется сделать это самим.

VIII. char *strchr (char *string, int c)

IX. char *strstr (char *string, char *substring)

ищут первое вхождение в строку string соответственно символа c и подстроки substring. Обе функции возвращают адрес первого вхождения или NULL, если такового не найдено.

Пример:

(void)

{

int

main

char

string[15];

 

char

*ptr,

c = 'r';

//Создаем

строку

"This is a string");

strcpy (string,

ptr = strchr(string, c);

if (ptr)

printf("The character %c is at position: %d\n", c, ptrstring);

else

printf( "The character was not found\n" ); return 0; }

На экране появится: The character r is at position: 12

59

Глава 10 ФAЙЛЫ

Файл характеризуется именем, расширением, версией.

MS DOS: длина имени <=8 символов, длина расширения <=3 символов. Файл: = <имя> [.<расширение>]

Proba.c

Устройство внешней памяти, строго говоря, является устройством поблочного обмена, т.е. за одно обращение к устройству производится считывание или запись фиксированной порции данных. Чаще всего минимальной порцией данных, участвующих в обмене с внешней памятью, являются блоки в 512 или 1024 байтов.

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

При вводе данных в файл они накапливаются в буфере, а при заполнении буфера они записываются в виде единого блока на диск за одно обращение к последнему.

чтение из файла

Жесткий диск

Буфер

ОЗУ

данные

ввод в файл

Жесткий диск

Буфер

ОЗУ

данные

Буферы операционной системы реализуются в виде участков основной памяти. Поэтому пересылка между буферами ввода–вывода и выполняемой программой происходит достаточно быстро в отличие от реальных обменов с физическими устройствами.

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

ПОТОК – ЭТО ФАЙЛ ВМЕСТЕ С ПРЕДОСТАВЛЯЕМЫМИ СРЕДСТВАМИ БУФЕРИЗАЦИИ.

ФАЙЛ + СРЕДСТВА_БУФЕРИЗАЦИИ = ПОТОК

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

Открывать и закрывать потоки (связывать указатели на потоки с конкретными файлами).

Вводить и выводить: символ, строку, форматированные данные, порцию данных произвольной длины.

60

Анализировать ошибки потокового ввода–вывода и условие достижения конца потока (конца файла).

Управлять буферизацией потока и размером буфера.

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

ка СИ, в программу необходимо включить заголовочный файл stdio.h (#include<stdio.h>), который содержит прототипы функций ввода–вывода, а также описания констант, типов и структур, необходимых для работы функций обмена с потоком.

12.1Открытие и закрытие потока

Подготовительные операции перед обменом программы с файлом:

1.Проверка наличия файла с данным именем при чтении.

2.Наличие свободного пространства на диске при создании файла.

3.Позиционирование файла.

При благополучном открытии файла ОС возвращает целое число, характеризующее этот поток и называемое ДЕСКРИПТОМ файла (потока).

FORTRAN READ(5,…) WRITE(6,…)

Процесс отсоединения файла от программы после окончания операций с ним называется закрытием файла. При этом освобождаются ресурсы (в основном память). С каждым потоком связан системный буфер, который для MS DOS составляет 512 байтов. Дескриптор – это тоже ресурс, поскольку их число для MS DOS ограничено 255. Также для каждого файла в памяти хранится его описание в виде структуры типа FILE, описанного в файле stdio.h.

РЕКОМЕНДАЦИЯ. Желательно закрывать файл явно по причинам:

1.Случайной порчи открытого файла.

2.Возможного повторного открытия того же файла с другим способом обработки.

3.При аварийном завершении программы информация, хранимая в системном буфере пропадает.

4.Возможность сэкономить на количестве открываемых потоков.

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

При открытии потока в программу возвращается указатель на поток, являю-

щийся указателем на объект структурного типа FILE. Этот указатель идентифицирует поток во всех последующих операциях.

Указатель на поток, например fp, должен быть (1) объявлен в программе следующим образом:

#include <stdio.h> FILE *fp;

61

Указатель на поток (2) приобретает значение в результате выполнения функцией открытия потока:

Формат:

<указатель_на_поток> = fopen (<имя_файла>, <режим_открытия>);

Параметры <имя_файла> и <режим_открытия> являются указателями на массивы символов (строки!), содержащие, соответственно, имя файла, связанного с потоком, и строку режимов открытия. Однако, эти параметры могут задаваться и непосредственно в виде строк при вызове функции открытия файла.

Например: fp = fopen ( “t.txt”, ”r” );

где t.txt –имя некоторого файла, связанного с потоком; r – обозначение одного из режимов работы с файлом (тип доступа к потоку).

Стандартный текстовый файл можно открыть в одном из следующих шести

режимов:

“w”–новый текстовый (см. ниже) файл открывается для записи. Если файл уже существовал, то предыдущее содержимое стирается и файл создается заново.

“r” – существующий текстовый файл открывается только для чтения.

“a” – текстовый файл открывается (или создается, если файла нет) для добавления в него новой порции информации (добавление в конец файла). В отличие от режима “w” режим “a” позволяет открывать уже существующий файл, не уничтожая его предыдущей версии, и писать в продолжение файла.

“w+” – новый текстовый файл откравается для записи и последующих многократных исправлений. Если файл уже существует, то предыдущее содержимое стирается. Последующие после открытия файла запись и чтение из него допустимы в любом месте файла, в том числе запись разрешается и в конец файла, т.е. файл может увеличиваться

(“расти”).

“r+” – существующий текстовый файл открывается как для чтения, так и для записи в любом месте файла, однако в этом режиме невозможна запись в конец файла, т.е. недупустимо увеличение размеров файла.

“a+” – текстовый файл открывается или создается (если файла нет) и становится доступным для изменений, т.е. для записи и для чтения в любом месте; при этом в отличие от режима “w+” можно открыть существующий файл и не уничтожать его содержимое; в отличие от режима “r+” в режиме “a+” можно ввести запись в конец файла, т.е. увеличивать его размер.

Поток можно открыть в текстовом или двоичном (бинарном) режиме.

Втекстовом режиме прочитанная из потока комбинация символов CR (значение

13)и LF (значение 10), т.е. управляющие коды операции “возврат каретки” и “перевод

строки”, преобразуется в один новый символ ‘\n’ (значение 10, совпадающее с LF). При записи в поток в текстовом режиме осуществляется обратное преобразование, т.е. символ новой строки ‘\n’ (LF) заменяется последовательностью CR и LF.

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

режима, который обозначается буквой b.

Например “r+b” или “wb”. В некоторых компиляторах текстовый режим обмена обозначается буквой t, т.е. записывают “a+t” или “rt”.

62

Если поток открыт для изменений, т.е. в параметре режима присутствует символ “+”, то разрешены как чтение из потока так и запись в него. Однако смена режима (переход от записи к чтению и обратно) должна происходить только после установки указателя потока в нужную позицию.

При открытии потока могут возникнуть следующие ОШИБКИ:

1.Указанный файл, связанный с потоком, не найден (для режима чтения).

2.Диск заполнен или защищен от записи и т.п.

3.Необходимо также отметить, что при выполнении функции fopen() происходит выделение динамической памяти. При ее отсутствии устанавливается признак ошибки ”Not entough memory” (недостаточно памяти).

В перечисленных случаях указатель на поток приобретает значение NULL. Заметим. Что указатель на поток в любом режиме, отличном от аварийного, никогда не бывает равным NULL.

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

if ( ( fp=fope(“t.txt”,”w”)) = = NULL ) { perror(“ошибка при открытии файла t.txt \n”); exit (0); }

где NULL – нулевой указатель, определенный в файле stdio.h.

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

void perror ( const char *s);

Функция perror( ) выводит строку символов, адресуемую указателем s, за которой размещаются: двоеточие, пробел и сообщение об ошибке. Содержимое и формат сообщения определяются реализацией системы программирования. Текст сообщения об ошибке выбирается функцией perror() на основании номера ошибки. Номер ошибки заносится в переменную int erno (определяется в заголовочном файле erno.h) рядом функций библиотеки языка СИ, в том числе и функциями ввода-вывода.

После того, как файл открыт, с ним можно работать, записывая в него информацию или считывая ее (в зависимости от режима).

Открытые на диске файлы рекомендуется ЗАКРЫВАТЬ ЯВНО. Для этого используется библиотечная функция.

int fclose (<указатель_на_поток>) ;

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

Пример:

FILE *f, *fp;

if (( f=fopen (“inp3.txt”,”rt”)) = = NULL ) { printf(“cannot open input file.\n”); return 1; }

fscanf ( f, ”%d”, &count ); fclose ( f );

if ( !(fp = fopen ("inp3.txt", "r") ) ) exit 0;

fscanf ( fp, ”%d”, &razruad); fclose ( fp );

63

12.2Работа с файлами на диске

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

fgetc( ) –ввод (чтение) одного символа из файла. fputc( ) –запись одного символа в файл. fprintf( ) –форматированный вывод в файл.

fscanf( ) –форматированный ввод (чтение) из файла.

fgets( ) –ввод (чтение) строки из файла. int fgets(FILE *stream); fputs( ) –запись одной строки в файл.

1.int fgetc(FILE *stream ) –чтение одного символа из файла.

int fputc(int c, FILE *stream) – запись одного символа в файл.

Например:

void main(void) { FILE *stream; char ch;

stream=fopen(“DYMMY.FIL”, “+w”); do{ ch=fgetc(stream);

putch(ch); } while (ch != EOF);

fclose(stream); }

Например:

#include<stdio.h> void main(void)

{ char msg[]=“hello, world”; int i=0;

while(msg[i])

fputc(msg[i], stdout); }

2. fprintf( ) форматированный вывод в файл. fscanf( ) форматированный ввод (чтение) из файла.

Например:

#include<stdio.h> void main(void) { FILE *stream; int i=100;

float f= 1.2345; char c=‘C’;

stream=fopen(“DYMMY.FIL”, “w+”); fprintf(stream, “%d %c %.4f”, i, c, f); fclose(stream); }

3. char *fgets(char *s, int n, FILE *stream) – функция чтения строки из файла. int fputs(const char *s, FILE *stream) – функция записи строки в поток.

64

Например:

void main (void) {FILE *stream; char msg[20];

stream=fopen(“DYMMY.FIL”, “w+”); fputs(“\nHello, WORLD”, stream); fclose(stream); stream=fopen(“DYMMY.FIL”, “r”); fgets(msg, strlen(str)+1, stream); printf(“%s”, msg); fclose(stream); }

В качестве примера использования функций getс() и putс() рассмотрим программы ввода данных в файл с клавиатуры и программу вывода их на экран дисплея из файла.

Программа ввода читает символы с клавиатуры и записывает их в файл.

Пусть признаком завершения ввода служит поступивший от клавиатуры символ

“#”.

Имя файла запрашивается у пользователя.

Если при вводе последовательности символов была нажата клавиша <Enter>, служащая разделителем строк при вводе с клавиатуры, то в файл записываются коды “возврат каретки” (CR – значение 13) и “перевод строки” (LF – значение 10). Код CR в дальнейшем при вызове вызывает перевод маркера (курсора) в начало строки экрана дисплея. Код LF служит для перевода маркера на новую строку дисплея. Значения этих кодов в тексте программы обозначены соответственно идентификаторами CR и LF, т.е. CR и LF

– именованные константы. Запись управляющих кодов CR и LF в файл позволяет при последующем вызове файла на экран отделить строки друг от друга.

Пример:

/*программа записи (ввода) символов в поток (файл)*/

#include<stdio.h> int main(){

FILE *fp;/*указатель на поток*/

char c, fname[20]; /*массив для имени файла*/

const char CR=‘\015’, /*восьмиричный код возврата карет-

ки*/

/*восьмеричный код перевода строки*/

LF=‘\012’;

/*запрос имени файла*/

 

 

puts (“Введите имя файла:\n”);

 

gets(fname);

для

записи*\

 

/*открыть файл

 

if ((fp=fopen(fname, “w”)) = = NULL) {

perror(fname);

 

 

return 1;

}

 

 

/*цикл ввода и записи в файл символов*\

while ((c=getch() ) != ‘#’)

{

if ( c == ‘\n’)

{

 

fputc(CR,fp);

65

fputc(LF,fp); } else { fputc(c,fp); putc(c); } }

/* цикл ввода завершен; закрыть поток: */ fclose (fp);

return 0; }

Пример:

/*программа вывода потока (файла) на экран дисплея*/

#include<stdio.h> int main(void) {

FILE *fp;/*указатель на поток*/ char c;

char fname[20]; /*массив для имени файла*/ /*запрос имени файла*/

puts(“введите имя файла:\n”); gets (fname);

/*открыть файл для чтения*/

if ( (fp=fopen(fname, “r”) ) == NULL)

{perror(fname);

return 1; }

/*цикл чтения из файла и вывода символов на экран*/ while ( (c=fgetc(fp) ) != EOF )

putchar (c);

fclose(fp); } /*закрыть файл*/

ЗАКЛЮЧИТЕЛЬНЫЙ ПРИМЕР ПО ТЕМЕ "ФАЙЛЫ"

//Дан символьный файл f.dat. Записать в файл:

//h_otr.dat все отрицательные компоненты исходного файла,

//h_por.dat компоненты исходного файла, упорядоченные по возрастанию,

//h_sum.dat сумму, произведение и сумму квадратов компонент исходного

файла.

# include <stdio.h> void main ()

{ int a[100], // массив компонент исх. файла perest, i, j, // вспомогательные переменные kol, // количество компонент исх. файла

sum, pr, sum_2; // Сумма, произведение и сумма квадратов

FILE *fil, *f;

// Очищаем файлы h_otr.dat, и h_por.dat, и h_sum.dat fil = fopen("h_otr.dat", "w");

fclose(fil);

fil = fopen("h_por.dat", "w"); fclose(fil);

fil = fopen( "h_sum.dat", "w" ); fclose (fil);

// Считываем из файла f.dat только отрицательные

66

// компоненты и заносим их в файл h_otr.dat

if ( ( fil = fopen ( "f.dat", "r" ) ) = = NULL)

{puts( " Ошибка " ); exit (0); }

f = fopen( "h_otr.dat", "w" );

while ( fscanf ( fil, "%d", & kol ) != EOF ) if ( kol < 0) fprintf ( f, "%d\n", kol );

fclose ( fil ); fclose ( f );

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

//по возрастанию заносим их в файл h_por.dat if ( (

fil = fopen ( "f.dat", "r" )

) = = NULL)

{ puts( " Ошибка "); exit (0); }

i = 0;

while ( ( a[i] = fgetc (fil) ) != EOF) i ++;

fclose (fil);

kol = i;

for ( i = 0; i < kol; i++ ) {

for ( j = i; j < kol – 1; j++ ) { if ( a[j] > a[j+1] ) {

perest = a[j]; a[j] = a[j+1];

a[j+1] = perest; } } }

fil = fopen( "h_por.dat", "w" ); for ( i = 0; i < kol; i++ )

fputc ( a[i], fil); fclose (fil);

/* Находим сумму, произведение и сумму квадратов компонент файла

f.dat, заносим их в файл h_sum.dat */

f = fopen( “h_sum.dat”, “w” ); sum = sum_2 = 0; pr = 1;

fil = fopen (“f.dat”, “r” );

for(i=0; ( fscanf(f, “%d”, &a[i])!=EOF); i++ ) ;

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

{

sum += a[i];

 

pr *= a[i];

}

sum_2 += a[i]*a[i];

fprintf(fil, “sum = %d

\t pr = %d \t sum_2 = %d \n”, sum,

pr, sum_2);

 

fclose (fil); }

 

67

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]