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

MM2 TP

.pdf
Скачиваний:
9
Добавлен:
09.02.2015
Размер:
3.63 Mб
Скачать

FLOAT X;

CHAR NAME[50];

SCANF("&D %F %S", &I, &X, NAME);

со строкой на вводе

25 54.32E-1 THOMPSON

приводит к присваиванию I значения 25,X - значения 5.432 и NAME - строки

"THOMPSON", надлежащим образом законченной символом \ 0. Эти три поля ввода мож-

но разделить столькими пробелами, табуляциями и символами новых строк, сколько по-

требуется с точки зрения дизайна.

Выполнение следующего фрагмента: INT I;

FLOAT X;

CHAR NAME[50];

SCANF("%2D %F %*D %2S", &I, &X, NAME);

с вводом

56789 0123 45A72

присвоит I значение 56, X - 789.0, пропустит 0123 и поместит в NAME строку "45".

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

В этих двух примерах NAME является именем массива и, следовательно, указателем. По этой причине перед ним ненужно помещать знак &.

В качестве примера рассмотрим элементарный калькулятор:

/* EX0022.C */ #include <stdio.h> main() {

double sum, v; sum =0;

while (scanf("%lf", &v) != EOF) printf("\t%.2f\n", sum = sum + v);

return(0);

}

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

щих и присвоенных элементов ввода. Это число может быть использовано для определе-

ния количества найденных элементов ввода. при выходе на конец файла возвращается

EOF. Подчеркнем, что это значение отлично от 0, что следующий вводимый символ не удовлетворяет первой спецификации в управляющей строке. При следующем обращении к SCANF поиск возобновляется непосредственно за последним введенным символом.

Заключительное предостережение: аргументы функции SCANF должны быть указате-

лями. Несомненно наиболее распространенная ошибка состоит в написании scanf("%d", n);

вместо scanf("%d", &n);

4.5Форматное преобразование в памяти

Возврат_к_оглавлению

От функции SCANF и PRINTF происходят функции SSCANF и SPRINTF, которые осуществляют аналогичные преобразования, но оперируют со строкой, а не с файлом. Об-

ращения к этим функциям имеют вид: SPRINTF(STRING, CONTROL, ARG1, ARG2, ...) SSCANF(STRING, CONTROL, ARG1, ARG2, ...)

Подобно PRINTF функция SPRINTF преобразует свои аргументы ARG1, ARG2 и т.д. в

соответствии с форматом, указанным в CONTROL, но помещает результаты в STRING, а

не в стандартный вывод. Существенно, чтобы строка STRING имела достаточный размер для размещения результата.

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

NAME - символьный массив

N - целое

Тогда SPRINTF(NAME, "TEMP%D", N); создает в NAME строку вида TEMPNNN, где

NNN - значение N.

Функция SSCANF выполняет обратные преобразования - она просматривает строку

STRING в соответствии с форматом в аргументе CONTROL и помещает результирующие значения в аргументы ARG1, ARG2 и т.д.эти аргументы должны быть указателями. В ре-

зультате работы оператора

SSCANF(NAME, "TEMP%D", &N);

переменная N получает значение строки цифр, следующих за TEMP в NAME.

Последствия появления в строке формата непустых символов, кроме символов специ-

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

рукций, подобных предыдущему примеру для SCANF, должно сопровождаться достаточ-

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

зультатов лучше таких конструкций избегать.

4.6Доступ к файлам

Возврат_к_оглавлению

До сих пор рассматривались примеры, в которых программы читали из стандартного ввода и писали в стандартный вывод, относительно которых предполагалось, что они пре-

доставлены программе местной операционной системой.

Следующим шагом в вопросе ввода-вывода является написание программы, работаю-

щей с файлом, который не связан заранее с программой.

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

Функция FOPEN берет внешнее имя (подобное X.C или Y.C), проводит некоторые об-

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

Это внутреннее имя, называемое "указателем файла", фактически является указателем структуры, которая содержит информацию о файле, такую как место размещения буфера,

текущая позиция символа в буфере, происходит ли чтение из файла или запись в него и тому подобное. Пользователи не обязаны знать эти детали, потому что среди определений для стандартного ввода-вывода, получаемых из файла STDIO.H, содержится определение структуры с именем FILE.

Далее приведен фрагмент программы, в котором выполняется открытие файла. file *fp;

fp = fopen(name,mode);

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

формацию об открываемом файле.

Вторая строка это вызов функции FOPEN, которая открывает файл. Первый аргумент

FOPEN – символьная строка, содержащая спецификацию файла (адрес в файловой систе-

ме). Второй аргумент также является символьной строкой, которая указывает, как этот файл будет использоваться. Допустимыми режимами являются: чтение ("R"), запись ("W")

и добавление ("A").

При попытке открыть файл, который еще не существует, для записи или добавления,

то такой файл будет создан (если это возможно). Открытие существующего файла на за-

пись приводит к отбрасыванию его старого содержимого. Попытка чтения несуществую-

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

(например, попыткой чтения из файла, не имея на то разрешения). При наличии какой-

либо ошибки функция возвращает нулевое значение указателя NULL (которое для удоб-

ства также определяется в файле STDIO.H).

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

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

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

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

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

лы потребляют ресурсы компьютера и своевременное закрытие файлов повышает эффек-

тивность вычислительного процесса.

Итак обращаем внимание на существование функции закрытия файла: fclose(fp);

Параметром функции является указатель на файл, который сформирован соответст-

вующей функцией FOPEN.

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

разом,

c=getc(fp)

помещает в C следующий символ из файла, указанного посредством FP, и EOF, если достигнут конец файла.

Функция PUTC putc(c,fp)

помещает символ C в файл FP и возвращает C.

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

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

стандартный вывод и стандартный вывод ошибок; соответствующие указатели файлов на-

зываются STDIN, STDOUT и STDERR. Обычно все эти указатели связаны с терминалом,

но STDIN и STDOUT могут быть перенаправлены на файлы с при запуске программы из командной строки.

Функции GETCHAR и PUTCHAR могут быть определены в терминалах GETC, PUTC, STDIN и STDOUT следующим образом:

#define getchar() getc(stdin) #define putchar(c) putc(c, stdout)

Следует помнить, что GETC, как и GETCHAR используют буферизацию, следователь-

но при использовании GETC для чтения с клавиатуры (из STDIN) фактический возврат символа происходит только после нажатия ENTER.

Для примера рассмотрим программу копирования символов из одного файла в другой

(EX0023.C). Этот пример целесообразно сравнить с примером EX0005.C, где выполнялось копирование символов с клавиатуры на экран.

/* EX0023.C */ #include <stdio.h>

int main() /* копирование символов */

{

FILE *f1, *f2; char c;

if ((f1 = fopen("c:\\pronc\\ex\\ex0023.tx1","r")) != NULL) {

if ((f2 = fopen("c:\\pronc\\ex\\ex0023.tx2","w")) != NULL) { c = getc(f1);

while (c != EOF) { putc (c,f2); c = getc(f1);

}

fclose(f1);

fclose(f2);

}

else

printf("ERROR ON OPENING OUTPUT FILE\n");

}

else

printf("ERROR ON OPENING INPUT FILE\n"); return(0);

}

Впримере EX0023.C спецификации открываемых файлов содержат двойные символы

\\вместо одинарных. Дело в том, что в языке C символ \, входящий в строку символов, яв-

ляется специальным. Он является началом специальных последовательностей, таких как

\n и т.п. Дублирование символа "отключает" его специальную функцию.

Для нормальной работы примера EX0023.C необходимо до его запуска сформировать файл EX0023.TX1, либо с помощью редактора NOTEPAD, либо WORD. В последнем слу-

чае необходимо сохранить набранный текст в файле типа ОБЫЧНЫЙ ТЕКСТ. В обоих случаях необходимо позаботиться чтобы сформированный файл имел требуемое расши-

рение (TX1). Особенности подготовки файла EX0023.TX1 с помощью WORD определя-

ются тем, что стандартная структура документа WORD (файлы с расширением DOC) со-

держат кроме текстовой информации большое количество служебной. Эта служебная ин-

формация представлена байтами, значение которых может не входить в диапазон значе-

ний представляющих текст. Следовательно при копировании файла программа EX0023.C

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

Для обеспечения копирования документов WORD преобразуем предыдущий пример к виду EX0024.C.

В этом примере изменен режим открытия файлов. Использованное обозначение (RB и WB) приводит к "байтовому" режиму работы с файлами при котором функции GETC и PUTC переписывают каждый байт информации без изменений. В обычном (текстовом)

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

В примере EX0024.C присутствует еще одно изменение. Оно связано с использовани-

ем функции FEOF для обнаружения конца файла. Эта функция срабатывает не по нали-

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

ластях внешнего носителя данных.

Символ ! встречающийся в условном выражении для WHILE означает логическое от-

рицание.

/* EX0024.C */ #include <stdio.h>

int main() /* копирование символов из одного документа

WORD в другой*/

{

FILE *f1, *f2; char c;

if((f1 = fopen("c:\\pronc\\ex\\ex00241.doc","rb")) != NULL) {

if((f2 = fopen("c:\\pronc\\ex\\ex00242.doc","wb")) != NULL) { c = getc(f1);

while (! feof(f1)) { putc(c,f2); c = getc(f1);

}

fclose(f1);

fclose(f2);

}

else

printf("ERROR ON OPENING OUTPUT FILE");

}

else

printf("ERROR ON OPEING INPUT FILE");

return(0);

}

Кроме ввода/вывода символов для обработки файлов можно использовать форматные функции FSCANF и FPRINTF. Они идентичны функциям SCANF и PRINTF, за исключе-

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

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

Для иллюстрации применения FPRINTF рассмотрим программу, реализующую чис-

ленное решение дифференциального уравнения. Для примера возьмем уравнения, описы-

вающие электрическую цепь, состоящую из последовательного соединения активного со-

противления R, емкости C и индуктивности L.

Зависимость тока в такой цепи I от приложенного напряжения U описывается сле-

дующим уравнением:

 

 

dI

 

 

 

 

 

1

t

 

 

 

 

 

 

 

L

+ RI +

òIdt =U

 

dt

C

 

 

 

 

 

 

 

 

0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

избавляемся от интеграла и делим обе части на L.

 

 

d 2 I

 

+

R dI

+

1

I =

1 dU

(4.6.1)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

dt 2

 

L dt

LC

L dt

 

 

 

 

 

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

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

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

 

x1 = I

 

 

 

 

 

(4.6.2)

 

x2

=

dI

-

1

U

(4.6.3)

 

 

 

 

 

 

 

dt

 

 

 

L

 

из (4.6.2)

 

 

dI

=

 

dx1

(4.6.4)

 

dt

 

dt

 

 

 

 

 

 

 

 

 

 

 

 

 

 

из (4.6.3)

 

 

dI

= x2

+

1

U

(4.6.5)

 

dt

 

 

 

 

 

 

 

 

 

L

 

 

d 2 I

=

 

dx

2

+

 

1 dU

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(4.6.6)

 

dt 2

 

 

 

 

 

 

 

L

 

dt

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

dt

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

после подстановки (4.6.4) в (4.6.5)

 

 

 

 

 

 

 

dx1

 

 

= x2 +

1

U

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(4.6.7)

 

dt

L

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

после подстановки (4.6.6), (4.6.6) и (4.6.2) в (4.6.1) получаем

 

 

dx

2

 

 

 

 

1 dU

 

 

 

 

 

R æ

 

 

 

1

ö

 

1

 

 

1 dU

 

 

 

 

 

 

+

 

 

 

 

 

 

 

 

 

 

 

+

 

 

 

ç x

 

+

 

U ÷ +

 

x

=

 

 

 

 

(4.6.8)

 

dt

L dt

 

 

 

 

 

 

L

 

L dt

 

 

 

 

 

 

 

 

L è

 

2

 

ø

 

LC 1

 

 

после преобразований

 

 

 

 

 

 

 

 

 

 

 

 

 

dx2

 

+

R

x

 

 

+

1

x = -

R

U

 

 

 

 

 

 

 

(4.6.9)

 

 

 

 

 

 

 

LC

 

 

 

 

 

 

 

 

 

 

 

dt

 

 

 

L

2

 

 

 

 

 

 

1

 

 

L2

 

 

 

 

 

 

 

 

объединяя (4.6.7) и (4.6.9) окончательно получаем систему из двух дифференциальных уравнений первого порядка, эквивалентную уравнению (4.6.1)

{

dx1

 

= x2

+

1

U

 

 

 

 

 

 

dt

 

 

 

 

 

 

 

 

 

 

 

 

L

 

 

 

(4.6.10)

dx2

 

 

 

 

 

 

 

R

 

= -

1

x1

-

x2

-

R

U

 

 

 

2

 

dt

 

 

 

LC

 

L

 

L

Метод Эйлера заключается в замене производной отношением приращений. После та-

кой операции можно записать:

x

 

= x

 

+ (x

2(k -1)

+

1

U

(k -1)

)Dt

 

 

 

 

 

 

 

 

 

 

 

 

 

1(k )

1(k -1)

 

 

L

 

 

 

 

 

 

 

{x

 

 

 

 

 

1

 

 

R

 

 

 

R

 

 

(4.6.11)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

= x

 

+ (-

x

-

x

 

-

U

 

)Dt

 

 

LC

 

 

L2

 

 

2( k )

 

2(k -1)

 

 

1(k -1)

 

L

 

2(k -1)

 

 

(k -1)

 

В программе EX0025.C рассчитывается переходный процесс установления тока в RLC

цепи при подаче на нее постоянного напряжения. Полученные значения тока в зависимо-

сти от времени записываются в файл. Содержимое этого файла может быть использовано для построения графика средствами EXCEL.

/* EX0025.C */ #include <stdio.h> int main() {

FILE *fp;

float t, tmax, dt, r, l, c, x1, x2;

tmax = 30.0;

dt

= 0.1;

r

= 1.0;

l

= 1;

c

= 1;

x1

= 0.0;

x2

= 0.0;

if((fp = fopen("c:\\pronc\\ex\\ex0025.txt","w")) != NULL) { for(t = 0.0; t <= tmax; t = t + dt) {

x1 = x1 + (x2 + 1.0/l) * dt;

x2 = x2 + ((-1.0/(l * c)) * x1 - (r/l) * x2 - r/(l * l)) * dt; fprintf(fp,"%f\t%f\t%f\n", t, x1, x2);

}

fclose(fp);

}

else

printf("ERROR ON OPEING OUTPUT FILE"); return(0);

}

Упражнение 19.

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

Упражнение 20.

Написать программу, которая генерирует файл, содержащий таблицу значений "за-

шумленной" синусоиды, т.е. таблицу значений функции SIN (t) + NOISE(t) . Программа должна запрашивать шаг таблицы, количество строк таблицы и амплитуду шума. На ос-

новании полученных данных построить график средствами EXCEL.

Упражнение 21.

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

граммы, созданной в упражнении 21. Программа должна запрашивать количество строк,

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

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