Функции построчного ввода:
char *gets (char *строка); - функция принимает байты со стандартного потока ввода stdin и размещает их по адресу, на который ссылается указатель строка. Прием символов завершается после считывания символа ’\n’ (нажатие ENTER), а сам этот символ преобразуется в ’\0’. Необходимо зарезервировать место для принимаемой строки.
char *fgets (char *строка, int колич_символов, FILE *имя_указателя); - функция принимает байты из открытого файла, на который ссылается указатель имя_указателя и размещает их по адресу, заданному указателем строка. Прием байтов завершается после получения колич_символов или получения символа ’\n’. Сам символ ’\n’ передается в строку. Последним символом в обоих случаях является ’\0’.
ВНИМАНИЕ
|
Перед началом использования необходимо зарезервировать место для строки. Функцию gets() следует использовать с осторожностью, потому что не всегда известно заранее, сколько символов введет пользователь, и вследствие этого функция может записать часть строки уже вне выделенного буфера. |
Пример:
FILE *fp;
char s[100];
fp=fopen (“dat.txt”,”r”);
fgets (s, 20, fp);
Пример: get.c - ввод строковых данных с клавиатуры с помощью функции gets()
#include <stdio.h>
char input[257];
int main( void )
{
puts("Enter some text, then press Enter: ");
gets(input);
printf("You entered: %s\n", input);
return 0;
}
Результат:
Enter some text, then press Enter:
This is a test
You entered: This is a test
Функции построчного вывода в случае успеха возвращают код последнего записанного в файл символа строки и EOF - в случае ошибки.
Функции построчного вывода:
int puts (char *строка); - функция помещает строку символов, на которую ссылается указатель строка, в стандартный поток вывода stdout.
int fputs (char *строка, FILE *имя_указателя); - функция помещает строку символов, на которую ссылается указатель строка, в открытый файл, на который ссылается указатель имя_указателя.
РЕКОМЕНДУЕТСЯ
|
Используйте функцию puts() вместо printf(), если необходимо отобразить текстовое сообщение без вывода каких-либо значений. |
НЕ РЕКОМЕНДУЕТСЯ
|
Не используйте спецификации ввода в функции puts(). |
Пример: put.c - отображение текста на экране с помощью функции puts()
#include <stdio.h>
char *message1 = "C";
char *message2 = "is the";
char *message3 = "best";
char *message4 = "programming";
char *message5 = "language!!";
int main( void )
{
puts(message1);
puts(message2);
puts(message3);
puts(message4);
puts(message5);
return 0;
}
Результат:
C
is the
best
programming
language!
13.3.3. Функции блокового ввода-вывода |
Функции блокового ввода-вывода также называют функциями прямого ввода-вывода.
Функции fread() и fwrite() позволяют выполнить перенос блока байтов (записей) между файлом и программой.
Блок – это группа расположенных подряд байтов, не имеющих никаких специальных разделителей.
Формат:
int fread (void *имя_указателя1, int размер, int количество, FILE *имя_указателя2);
Эта функция считывает количество элементов размером размер байт каждый в область памяти, определяемую указателем имя_указателя1, из открытого файла, на который ссылается указатель имя_указателя2. Функция возвращает число прочитанных байтов и EOF – в противном случае.
Формат:
int fwrite (void *имя_указателя1, int размер, int количество, FILE *имя_указателя2);
Эта функция записывает количество элементов размером размер байт каждый из области памяти, определяемую указателем имя_указателя1, в открытый файл, описываемый переменной типа FILE, на которую указывает указатель имя_указателя2.
Пример: считывание целого числа из файла
int num;
FILE *fp;
fp=fopen (“dat”,”r”);
. . .
fread (&num, sizeof (int), 1, fp);
Также функции fread() и fwrite() могут использоваться для записи и чтения пользовательских типов.
Пример:
FILE *fp;
fp= fopen (“dat”,”r”);
struct st1
{
float b;
char name[80];
} cust;
fread (&cust, sizeof (struct st1), 1, fp);
Пример: direct.c - использование функций fwrite() и fread() для блочного ввода-вывода
#include <stdlib.h>
#include <stdio.h>
#define SIZE 20
int main( void )
{
int count, array1[SIZE], array2[SIZE];
FILE *fp;
for (count = 0; count < SIZE; count++)
array1[count] = 2 * count;
if ( (fp = fopen("direct.txt", "wb")) == NULL)
{
fprintf(stderr, "Error opening file.");
exit(1);
}
if (fwrite(array1, sizeof(int), SIZE, fp) != SIZE)
{
fprintf(stderr, "Error writing to file.");
exit(1);
}
fclose(fp);
if ( (fp = fopen("direct.txt", "rb")) == NULL)
{
fprintf(stderr, "Error opening file.");
exit(1);
}
if (fread(array2, sizeof(int), SIZE, fp) != SIZE)
{
fprintf(stderr, "Error reading file.");
exit(1);
}
fclose(fp);
for (count = 0; count < SIZE; count++)
printf("%d\t%d\n", array1[count], array2[count]);
return 0;
}
Результат:
0 0
2 2
4 4
6 6
8 8
10 10
12 12
14 14
16 16
18 18
20 20
22 22
24 24
26 26
28 28
30 30
32 32
34 34
36 36
38 38
Также при блоковом вводе-выводе могут использоваться функции getw() и putw().
Формат:
int getw (FILE *имя_указателя); - возвращает число int, считанное из открытого файла
putw (int число, FILE *имя_указателя); - записывает число в открытый файл, на который ссылается указатель имя_указателя.
Пример:
int num;
FILE *fp;
fp=fopen (“dat”,”r”);
num=getw(fp);
13.3.4. Функции форматированного ввода-вывода |
Функции форматированного ввода-вывода представляют собой частный случай строкового ввода-вывода. Основная особенность заключается в том, что при вводе осуществляется специальное преобразование байтов строки и присваивание полученного значения специфицированным переменным. При выводе выполняется обратное преобразование значений и их символьное представление помещается в поток вывода.
Функции форматированного ввода-вывода являются функциями с переменным числом аргументов, первым из которых всегда является строка, задающая формат преобразования информации, которая состоит из следующих полей:
%[флаги][ширина][.точность][модификаторы]<тип>
Флаги: «+», «-», «#», пробел.
Модификаторы: «F», «N», «h» «l», , «L».
Наиболее употребляемые управляющие последовательности указаны в таблице 3.
Таблица 3
Последовательность |
Значение |
\a |
Звуковой сигнал (предупреждение) |
\b |
Возврат на один символ назад |
\f |
Прогон страницы |
\n |
Перевод строки |
\r |
Возврат каретки |
\t |
Горизонтальная табуляция |
\v |
Вертикальная табуляция |
\\ |
Обратная косая черта |
\? |
Знак вопроса |
\’ |
Одинарная кавычка |
\” |
Двойная кавычка |
“ |
Начало или конец текстовой строки |
Наиболее часто используемые спецификации ввода указаны в таблице 4.
Таблица 4
Спецификация |
Значение |
Типы выводимых переменных |
%c |
Одиночный символ |
char |
%d |
Десятичное целое число со знаком |
int, short |
%ld |
Десятичное длинное целое число со знаком |
long |
%f |
Десятичное число с плавающей точкой |
float |
%s |
Строка символов |
массивы типа char |
%u |
Десятичное целое число без знака |
unsigned int, unsigned short |
%lu |
Десятичное длинное целое число без знака |
unsigned long |
Функции ввода:
fscanf (); - вводит из заданного потока
sscanf (); - вводит из строки символов Си
scanf (); - вводит из стандартного потока ввода stdin
Функции вывода:
printf (); - выводит в стандартный поток вывода stdout
vprintf (); - аналогично предыдущей функции
fprintf (); - выводит в заданный поток
vfprintf (); - аналогично предыдущей функции
sprintf (); - выводит в строку символов Си
vsprintf (); - аналогично предыдущей функции
РЕКОМЕНДУЕТСЯ
|
Обязательно используйте символ конца строки при выводе нескольких строк информации отдельными функциями printf(). |
НЕ РЕКОМЕНДУЕТСЯ
|
Не пытайтесь выводить несколько строк текста одним вызовом функции printf(). В большинстве случаев удобно вывести несколько строк несколькими операторами, чем обходиться одним вызовом функции с несколькими символами конца строки «\n». |
Пример:
printf (“Это символ: %c, \nЭто число: %d\nЭто число с плав. \
точкой: %f\n”, ‘z’, 123, 456.789);
Результат:
Это символ: z
Это число: 123
Это число с плав. точкой: 456.789
ПРИМЕЧАНИЕ
|
В предыдущем примере можно заметить, что строка формата в функции printf() переносится на следующую строку. В конце первой строки кода косая черта «\» указывает, что строка формата продолжается в следующей строке кода. Компилятор воспринимает обе части строки формата как единое целое. |
Пример:
#include <stdio.h>
int main (void)
{
char symb=’S’;
int i=25;
printf (“Это пример вывода\n“);
printf (“Символ: %c\n Число: %d\n“, symb, i);
return 0;
}
Результат:
Это пример вывода
Символ: S
Число: 25
Пример: fprintf.c - эквивалентность форматированного ввода в файл и в поток stdout
#include <stdlib.h>
#include <stdio.h>
void clear_kb(void);
int main( void )
{
FILE *fp;
float data[5];
int count;
char filename[20];
puts("Enter 5 floating-point numerical values.");
for (count = 0; count < 5; count++)
scanf("%f", &data[count]);
clear_kb();
puts("Enter a name for the file.");
gets(filename);
if ( (fp = fopen(filename, "w")) == NULL)
{
fprintf(stderr, "Error opening file %s.", filename);
exit(1);
}
for (count = 0; count < 5; count++)
{
fprintf(fp, "\ndata[%d] = %f", count, data[count]);
fprintf(stdout, "\ndata[%d] = %f", count, data[count]);
}
fclose(fp);
printf("\n");
return 0;
}
void clear_kb(void)
{
char junk[80];
gets(junk);
}
Результат:
Enter 5 floating-point numerical values.
3.14159
9.99
1.50
3.
1000.0001
Enter a name for the file.
numbers.txt
data[0] = 3.141590
data[1] = 9.990000
data[2] = 1.500000
data[3] = 3.000000
data[4] = 1000.000122
РЕКОМЕНДУЕТСЯ
|
Используйте функции printf() и puts() в связке со scanf(). Сначала выведите сообщение о том, какие данные должен ввести пользователь, а затем введите эти данные функцией scanf(). |
|
НЕ РЕКОМЕНДУЕТСЯ
|
Не забывайте использовать знак операции взятия адреса «&» перед переменными в функции scanf(). |
Пример:
#include <stdio.h>
int main (void)
{
int num;
scanf (“%d“, &num);
printf (“\nВы ввели число: %d“, num);
return 0;
}
Результат:
// введено число 17 с клавиатуры
Вы ввели число: 17
Пример: scanit.c - ввод числовых данных с использованием функции scanf()
#include <stdio.h>
#define QUIT 4
int get_menu_choice( void );
int main( void )
{
int choice = 0;
int int_var = 0;
float float_var = 0.0;
unsigned unsigned_var = 0;
while (choice != QUIT)
{
choice = get_menu_choice();
if (choice == 1)
{
puts("\nEnter a signed decimal integer (i.e. -123)");
scanf("%d", &int_var);
}
if (choice == 2)
{
puts("\nEnter a decimal floating-point number(e.g. 1.23)");
scanf("%f", &float_var);
}
if (choice == 3)
{
puts("\nEnter an unsigned decimal integer (e.g. 123)" );
scanf( "%u", &unsigned_var );
}
}
printf("\nYour values are: int: %d float: %f unsigned: %u \n", int_var, float_var, unsigned_var );
return 0;
}
int get_menu_choice( void )
{
int selection = 0;
do
{
puts( "\n1 - Get a signed decimal integer" );
puts( "2 - Get a decimal floating-point number" );
puts( "3 - Get an unsigned decimal integer" );
puts( "4 - Quit" );
puts( "\nEnter a selection:" );
scanf( "%d", &selection );
}while ( selection < 1 || selection > 4 );
return selection;
}
Пример: input.c - ввод числовой и текстовой информации с помощью функции scanf()
#include <stdio.h>
char lname[257], fname[257];
int count, id_num;
int main( void )
{
puts("Enter last name, first name, ID number separated");
puts("by spaces, then press Enter.");
count = scanf("%s%s%d", lname, fname, &id_num);
printf("%d items entered: %s %s %d \n", count, fname, lname, id_num);
return 0;
}
Результат:
Enter last name, first name, ID number separated
by spaces, then press Enter.
Corey Taylor 12345
3 items entered: Corey Taylor 12345
ВНИМАНИЕ
|
Пользуйтесь функцией scanf() с осторожностью. Если, например, ожидается ввод символа, а пользователь введет число, или если нужно число, а вводится символ, то программа может выдать пользователю весьма неожиданные результаты. |
Пример: fscanf.c - чтение форматированных данных из файла с помощью функции fscanf()
#include <stdlib.h>
#include <stdio.h>
int main( void )
{
float f1, f2, f3, f4, f5;
FILE *fp;
if ( (fp = fopen("INPUT.TXT", "r")) == NULL)
{
fprintf(stderr, "Error opening file.\n");
exit(1);
}
fscanf(fp, "%f %f %f %f %f", &f1, &f2, &f3, &f4, &f5);
printf("The values are %f, %f, %f, %f, and %f\n.", f1, f2, f3, f4, f5);
fclose(fp);
return 0;
}
Результат:
The values are 123.49997, 87.00999, 100.019997, 0.004560, and 1.0005000
ПРИМЕЧАНИЕ
|
Неточность машинного представления числа является причиной того, что выведенные значения отличаются от введенных на очень маленькие величины. Например, 100.2 выводится как 100.01999. |
РЕКОМЕНДУЕТСЯ
|
Используйте функции gets() и scanf() вместо fgets() и fscanf(), если ввод должен выполняться только из стандартного потока stdin. |
НЕ РЕКОМЕНДУЕТСЯ
|
Не забывайте избавляться от лишних символов в потоке ввода. |
Пример: printer.c - вывод данных на принтер
#include <stdio.h>
int main( void )
{
float f = 2.0134;
fprintf(stdprn, "\n Это сообщение распечатается на принтере.\r\n");
fprintf(stdprn, "And now some numbers:\r\n");
fprintf(stdprn, "The square of %f is %f.", f, f*f);
fprintf(stdprn, "\f");
return 0;
}
Результат:
Следующие строки распечатаются на принтере
Цифры также выведутся на принтер:
Квадрат 2.013400 это 4.053780
ПРИМЕЧАНИЕ
|
Чтобы использовать поток stdprn, необходимо отказаться от совместимости с ANSI в вашем компиляторе. Обратитесь к документации компилятора за более подробной информацией. Поскольку поток stdprn не определен в стандарте ANSI, не каждый компилятор его поддерживает. |
Пример: escape.c - использование специальных символов в функции printf()
#include <stdio.h>
#define QUIT 3
int get_menu_choice( void );
void print_report( void );
int main( void )
{
int choice = 0;
while (choice != QUIT)
{
choice = get_menu_choice();
if (choice == 1)
printf("\nBeeping the computer\a\a\a" );
else
{
if (choice == 2)
print_report();
}
}
printf("You chose to quit!\n");
return 0;
}
int get_menu_choice( void )
{
int selection = 0;
do
{
printf( "\n" );
printf( "\n1 - Beep Computer" );
printf( "\n2 - Display Report");
printf( "\n3 - Quit");
printf( "\n" );
printf( "\nEnter a selection:" );
scanf( "%d", &selection );
}while ( selection < 1 || selection > 3 );
return selection;
}
void print_report( void )
{
printf( "\nSAMPLE REPORT" );
printf( "\n\nSequence\tMeaning" );
printf( "\n=========\t=======" );
printf( "\n\\a\t\tbell (alert)" );
printf( "\n\\b\t\tbackspace" );
printf( "\n...\t\t...");
}
РЕКОМЕНДУЕТСЯ
|
Используйте функцию fprintf() при написании ваших программ, посылающих целевой вывод в поток stdout, stderr, stdprn. |
Пример: num.c - ввод числовых значений с помощью функции printf()
#include <stdio.h>
int a = 2, b = 10, c = 50;
float f = 1.05, g = 25.5, h = -0.1;
int main( void )
{
printf("\nDecimal values without tabs: %d %d %d", a, b, c);
printf("\nDecimal values with tabs: \t%d \t%d \t%d", a, b, c);
printf("\nThree floats on 1 line: \t%f\t%f\t%f", f, g, h);
printf("\nThree floats on 3 lines: \n\t%f\n\t%f\n\t%f", f, g, h);
printf("\nThe rate is %f%%", f);
printf("\nThe result of %f/%f = %f\n", g, f, g / f);
return 0;
}
Результат:
Decimal values without tabs: 2 10 50
Decimal values with tabs: 2 10 50
Three floats on 1 line: 1.050000 25.500000 -0.100000
Three floats on 3 line:
1.050000
25.500000
-0.100000
The rate is 1.050000%
The result of 25.500000/1.050000 = 24.285715
СОВЕТ
|
Для более подробного ознакомления с функциями форматированного ввода-вывода обратитесь к справочной системе Си. |
13.4. Последовательный и прямой доступ в поток |
Операции чтения и записи для потока начинается с текущей позиции потока, определяемой указателем потока. Начальная позиция указателя потока устанавливается при открытии потока и может указывать на начало в режиме доступа «r» и «w», и на конец - в режиме «а».
После выполнения операции чтения или записи указатель потока изменяется и отражает новую текущую позицию потока, это т.н.последовательный доступ
Указатель потока можно позиционировать на любое место в потоке, т.е. осуществлять прямой доступ
Для управления указателем потока используются следующие функции:
ftell(); или fgetpos(); - определяют текущую позицию указателя потока
fseek(); или fsetpos(); - устанавливают указатель потока в заданную позицию
rewind(); - устанавливает указатель потока в начальную позицию
Формат:
void rewind (FILE *имя_указателя);
Формат:
int fseek (FILE *имя_указателя, long колич_байт, int из_положения);
где
имя_указателя – это указатель, ссылающийся на соответствующий файл.
Данная функция сдвигает указатель потока на число байт, равное аргументу колич_байт от точки отсчета, задаваемой аргументом из_положения.
ПРИМЕЧАНИЕ
|
Если параметр колич_байт>0, то указатель потока сдвигается в сторону конца файла. Если параметр колич_байт<0, то указатель потока сдвигается в сторону начала файла. |
Аргумент из_положения – это один из макросов, определенных в «stdio.h», определяющих точку отсчета для сдвига и может принимать одно из следующих значений:
SEEK_SET - начало потока
SEEK_CUR - текущая позиция потока
SEEK_END - конец потока).
В случае успеха функция fseek() возвращает ноль, а при ошибке – EOF.
Пример:
FILE *fp;
long n;
fp= fopen (“dat”,”r”);
n=14;
fseek (fp, n, SEEK_SET);
fseek (fp, 0L, SEEK_SET);
fseek (fp, -n, SEEK_END); // n-й элемент с конца
Пример: функция для чтения группы байтов с заданной позиции потока
int readbytes (FILE *fp, long pos, int n, char *save)
{
int num=-1;
if (fseek (fp, pos, SEEK_SET)==0)
num=fread (save, 1, n, fp);
return num;
}
Для определения текущей позиции потока используется функция ftell().
Формат:
long ftell (FILE *имя_указателя);
Данная функция возвращает текущее значение указателя записи/чтения, который имеет тип long int. В случае ошибки функция возвращает «-1» типа long, т.е. «-1L».
При использовании fseek() и ftell() следует учитывать преобразование символа ‘\n’ в два символа – «возврат каретки» и «перевод строки». Поэтому, задавая значения параметров функции fseek() или интерпретируя полученные результаты от ftell() следует помнить о дополнительных байтах в файле.
Например, запись 10 строк по 20 символов в каждой в поток, открытый в текстовом режиме, даст файл общей длинной (10*20)+10=210 байт (вместо предполагаемых 200 байт), а обращение «fseek(fp, 42, SEEK_SET);» установит указатель потока на конец 2-й строки, а не на начало 3-й.
ВНИМАНИЕ
|
Нельзя использовать функции определения и установки указателя потока для тех потоков, которые представляют устройства, а применение этих функций к стандартным потокам приведет к неопределенным результатам. |
Пример: ftell.c - использование функций rewind() и ftell()
#include <stdlib.h>
#include <stdio.h>
#define BUFLEN 6
char msg[] = "abcdefghijklmnopqrstuvwxyz";
int main( void )
{
FILE *fp;
char buf[BUFLEN];
if ( (fp = fopen("TEXT.TXT", "w")) == NULL)
{
fprintf(stderr, "Error opening file.");
exit(1);
}
if (fputs(msg, fp) == EOF)
{
fprintf(stderr, "Error writing to file.");
exit(1);
}
fclose(fp);
if ( (fp = fopen("text.txt", "r")) == NULL)
{
fprintf(stderr, "Error opening file.");
exit(1);
}
printf("\nImmediately after opening, position = %ld", ftell(fp));
fgets(buf, BUFLEN, fp);
printf("\nAfter reading in %s, position = %ld", buf, ftell(fp));
fgets(buf, BUFLEN, fp);
printf("\n\nThe next 5 characters are %s, and position now = %ld",
buf, ftell(fp));
rewind(fp);
printf("\n\nAfter rewinding, the position is back at %ld",
ftell(fp));
fgets(buf, BUFLEN, fp);
printf("\nand reading starts at the beginning again: %s\n", buf);
fclose(fp);
return 0;
}
Скачать архив [ftell.c, ftell.exe и text.txt]
Результат:
Immediately after opening, position = 0
After reading in abcde, position = 5
The next 5 characters are fhig, and position now = 10
After rewinding, the position is back at 0
and reading starts at the beginning again: abcde
Пример: fseek.c - произвольный доступ к файлу при помощи fseek()
#include <stdlib.h>
#include <stdio.h>
#define MAX 50
int main( void )
{
FILE *fp;
int data, count, array[MAX];
long offset;
for (count = 0; count < MAX; count++)
array[count] = count * 10;
if ( (fp = fopen("random.dat", "wb")) == NULL)
{
fprintf(stderr, "\nError opening file.");
exit(1);
}
if ( (fwrite(array, sizeof(int), MAX, fp)) != MAX)
{
fprintf(stderr, "\nError writing data to file.");
exit(1);
}
fclose(fp);
if ( (fp = fopen("RANDOM.DAT", "rb")) == NULL)
{
fprintf(stderr, "\nError opening file.");
exit(1);
}
while (1)
{
printf("\nEnter element to read, 0-%d, -1 to quit: ",MAX-1);
scanf("%ld", &offset);
if (offset < 0)
break;
else if (offset > MAX-1)
continue;
if ( (fseek(fp, (offset*sizeof(int)), SEEK_SET)) != 0)
{
fprintf(stderr, "\nError using fseek().");
exit(1);
}
fread(&data, sizeof(int), 1, fp);
printf("\nElement %ld has value %d.", offset, data);
}
fclose(fp);
return 0;
}
Результат:
Enter element to read, 0-49, -1 to quit: 5
Element 5 has value 50.
Enter element to read, 0-49, -1 to quit: 6
Element 6 has value 60.
Enter element to read, 0-49, -1 to quit: 49
Element 49 has value 490.
Enter element to read, 0-49, -1 to quit: 1
Element 1 has value 10.
Enter element to read, 0-49, -1 to quit: 0
Element 0 has value 0.
Enter element to read, 0-49, -1 to quit: -1
13.5. Управление буферизацией |
Потоки, открываемые функциями ввода-вывода для потоков, по умолчанию буферизируются, т.е. при открытии потока с ним связывается область памяти, называемая буфером.
При операции чтения блок данных помещается в буфер, а затем данные читаются из буфера. После обработки входного буфера в него помещается следующий блок данных.
При операции записи буфер выгружается (содержание буфера помещается в соответствующий поток) в следующих случаях:
Буфер заполнен
Закрывается связанный с ним поток
При успешном завершении программы
Буферизация повышает эффективность ввода-вывода, т.к. ОС передает за одну операцию большие блоки данных, а не выполняет операцию ввода-вывода каждый раз, когда из потока читается (в поток записывается) элемент данных.
Размер внутреннего буфера библиотечных функций составляет 512 байт для регулярных файлов и 128 байт – для стандартных файлов. Буферизацией ввода-вывода управляют функции setbuf() и setvbuf(), позволяющие либо отключить буферизацию полностью, либоиспользовать в качестве буфера область памяти прикладной программы (последнее, дает возможность существенно сократить объем памяти, необходимый программе при выполнении). Эти функции содержатся в «stdio.h».
Буферы, предоставленные ОС, пользователю не доступны, а с буферами, распределенные функциями setbuf() и setvbuf(), можно работать как с обычными переменными.
Формат: