Студопедия

КАТЕГОРИИ:


Архитектура-(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)

Ввод и вывод в стандартные файлы




Разработчики операционной системы Unix кроме "запрятывания" управляющих блоков файлов ввели еще одно новшество, связанное с этими блоками. Так как управляющие блоки файлов описывает и создает сама операционная система, то оказалось возможным поручить ей создавать управляющие блоки некоторых файлов перед запуском программы, точнее в ходе такого запуска. Кандидатами на автоматическое открытие стали обобщенные файлы, связанные с клавиатурой и экраном. Начинающие программисты должны помнить, что и в системах программирования на языках Паскаль и Си для операционных систем MS Windows имеется возможность открывать файлы не только по указанному собственному имени файла, но и использование специальные обозначения для некоторых устройств, в частности, для клавиатуры и экрана монитора (имеющих для этой цели служебное наименование CON). В свое время разработчиками было осознано, что в операционную систему можно предусмотрительно заложить открытие файлов для этих устройств. Возникает вопрос, как программист для создаваемой программы должен узнать значения хэндлов, которые образует ОС при таком предусмотрительном открытии. Было принято простое (и теперь, кажется, очевидное) решение: так как никакие файлы для запускаемой программы до подобного открытия не могли быть открыты по инициативе последней, то можно поручить ОС разместить дескрипторы файлов (управляющие блоки) в фиксированных позициях внутренней таблицы для таких дескрипторов. Поэтому оказалось возможным зарезервировать определенные номера хэндлов за автоматически открываемыми файлами для консоли и экрана.

Разработчики Unix пошли дальше и сняли ограничение, что эти файлы со стандартным размещением дескрипторов должны быть всегда связаны с клавиатурой и экраном. Решили, что они связываются всегда за исключением таких случаев, когда специальными деталями строки вызова программы не указанно что-то другое. Более того, решили, что два стандартно открываемых дескриптора файлов это слишком ограничено, и зарезервировали для этих целей три. В традиционных обозначениях этим дескрипторам соответствует стандартный ввод, стандартный вывод и стандартный вывод ошибок. Для обозначения высокоуровневого доступа к этим дескрипторам через буферизованный ввод-вывод, осуществляемый с помощью указателей типа FILE*, в Unix приняты символические обозначения stdin, stdout и stderr. Фактическое значение хэндлов, соответствующих этим указателям, для доступа через перечисленные дескрипторы стандартного ввода и вывода в Unix – всегда постоянное и, составляет, соответственно 0, 1, 2.

Таким образом, с первых же строк программы для ввода данных в нее можно пользоваться функциями ввода с помощью хэндла, задав его принудительно константой 0. (Такая возможность есть в Unix, OS/2, MS DOS и некоторых других ОС.) Аналогично для вывода можно воспользоваться функциями вывода с помощью хэндла, задав его константой 1. Построив программу с помощью этих допущений и обозначений, в дальнейшем программу можно вызывать по ее имени, и тогда ввод и вывод в этих программных функциях будут осуществляться с клавиатуры и на экран. Если же пользователь такой программы хочет передавать в нее данные вместо клавиатуры из файла с именем имяфайлаввода, то достаточно при вызове программы в командной строке написать

имяпрограммы < имяфайлаввода

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

имяпрограммы > имяфайлавывода

Наконец приказ, переназначить ввод с клавиатуры на файл < имяфайлаввода, а вывод на файл имяфайлавывода, запишется в командной строке текстом

имяпрограммы < имяфайлаввода > имяфайлавывода

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

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

В Unix для этих целей предназначены универсальные системные функции с прототипами

unsigned int read(int handle, void* buffer, unsigned int len),

unsigned int write(int handle, void* buffer, unsigned int len).

Здесь аргумент handle – хэндл файла, второй аргумент buffer - адрес буфера для чтения или соответственно записи данных, последний аргумент len – запрашивае­мое для чтения или задаваемое для записи число байтов. Функции возвращают число байтов, которые им удалось передать при вводе или выводе, в частности, функция read возвращает число байтов, прочитанных из файла. Это число может не совпадать с запрошенным, если ввод осуществляется с клавиатуры и завершается символом Enter либо при вводе из обычного файла обнаруживается конец файла.

Следующий пример, приведенный в листинге 2.2.1, демонстрирует простейшее применение этих функций в демонстрационной Unix программе, которая выдает приглашение на ввод текста, и вводимый далее текст выводит с примечанием, что именно он введен в программу.

#include <stdio.h>

void main()

{int len;

char buffer[100]="It was readed... ";

write(1,"Vvedite\n",8);

len=read(0, buffer+18, 80);

len += 18;

write(1, buffer, len);

}

Листинг 2.2.1. Программа для Unix

В операционной системе OS/2 для обозначения стандартного ввода и вывода также использовались фиксированные значения хэндла, равные 0 и 1, а сами универсальные системные функции ввода и вывода файлов имели прототипы

APIRET DosRead(HFILE hFile, VOID *buffer, ULONG len, ULONG *pactualen);

APIRET DosWrite(HFILE hFile, VOID *buffer, ULONG len,

ULONG *pactualen).

Здесь аргументы hFile задавали хэндлы файлов, с которыми выполняются операции чтения или записи, buffer – адреса буфера данных для чтения или записи, аргумент len – число запрашиваемых при вводе или задаваемых для вывода байтов, а pactualen являлся указателем на длинное слово данных для выдачи (в качестве вспомогательного результата) числа переданных байтов.

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

HANDLE GetStdHandle(DWORD nStdHandle),

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

#define STD_INPUT_HANDLE (DWORD)-10

#define STD_OUTPUT_HANDLE (DWORD)-11

#define STD_ERROR_HANDLE (DWORD)-12

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

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

BOOL WINAPI ReadFile(HANDLE hFile, void* buffer, DWORD len,

DWORD *pactualen, OVERLAPPED *pOverlapped);

BOOL WINAPI WriteFile(HANDLE hFile, void* buffer, DWORD len,

DWORD *pactualen, OVERLAPPED *pOverlapped),

причем первый из аргументов этих функций задает хэндл (для наших ближайших целей – хэндл файла), второй – buffer – определяет место (буфер) для считываемых данных, аргумент len задает число байтов, которые запрашиваются для ввода или задаются для вывода. Аргумент pactualen – возвращаемый параметр, он определяет (после выполнения системной функции) сколько байтов было действительно прочитано (собственно аргумент задается адресом места для записи возвращаемого значения). Последний аргумент pOverlapped имеет достаточно специальный характер и в нашем изложении не будет использоваться, а его значение будет задаваться как NULL. Число прочитанных байтов может быть меньше числа запрошенных, если при чтении запрошенных обнаружен конец файла.

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

#include <windows.h>

#include <stdio.h>

void main()

{char buffer[100]="It was readed...";

DWORD actlen;

HANDLE hstdin, hstdout;

BOOL rc;

hstdout = GetStdHandle(STD_OUTPUT_HANDLE);

if (hstdout = = INVALID_HANDLE_VALUE) return;

hstdin = GetStdHandle(STD_INPUT_HANDLE);

if (hstdin = = INVALID_HANDLE_VALUE) return;

rc=ReadFile(hstdin, buffer+18, 80, &actlen, NULL);

if (!rc) return;

actlen += 18;

WriteFile(hstdout, buffer, actlen, &actlen, 0);

getchar();

}

Листинг 2.2.2. Программа для Windows

 

Непосредственно видно, что программа Windows для решения той же самой простейшей задачи получается заметно сложнее, чем для других ОС. Кроме собственно запроса и проверки получения хэндлов стандартных ввода и вывода в этой программе оказывается практически необходимым дополнительно приостанавливать ее работу на вспомогательном вводе произвольного символа. Такой прием оказывается вынужденным, если данную программу запускать под управлением операционных систем типа Windows 9x. В этих ОС отсутствует постоянно задаваемое текстовое окно консольного вывода, и поэтому запуск консольного приложения из графического режима после выполнения программы приводит к свертыванию временно созданного для нее окна, и пользователь не успевает просмотреть окончательные результаты, выдаваемые такой программой. Приостановка выполнения программы с помощью функции getchar(), входящей в стандартную библиотеку языка Си, требует подключения заголовочного файла stdio.h. Заметим, что подобная программа, ориентированная исключительно на разновидности Windows NT, может использоваться без вспомогательного оператора getchar().

При использовании в программах на языке Си стандартного вывода нужно учитывать следующую существенную особенность. Ряд функций вывода из стандартной библиотеки языка Си используют так называемый буферизованный вывод. К этим функциям относятся все те программные функции, которые явно или неявно используют для обозначения файлов указатели типа FILE*. В частности, к ним относятся функции printf() и fprintf(). Для более эффективной работы в общем случае эти функции осуществляют собственно вывод только после заполнения внутреннего буфера или явным указанием его опустошения (flush). Такая внутренняя логика функционирования приводит иногда к тому, что начинающий программист получает на выводе не совсем тот результат, который ожидает (или даже совсем не тот!).

Так совместное использование вызовов функций WriteFile и printf в программе для Windows может приводить к тому, что в результирующий файл попадают вначале все данные от выполняемых функций WriteFile, а только затем – от функций printf. Этот эффект легко заметить, если использовать переадресацию стандартного вывода в следующей программе из листинга 2.2.3.

 

#include <windows.h>

#include <stdio.h>

int main()

{HANDLE hout;

DWORD actlen;

hout=GetStdHandle(STD_OUTPUT_HANDLE);

printf("Один...\n"); // fflush(stdout);

WriteFile(hout,"Два...",6,&actlen,0);

printf("Три\n"); // fflush(stdout);

WriteFile(hout,"Четыре...",9,&actlen,0);

return 0;

}

Листинг 2.2.3. Совместное использование буферизованных

и небуферизованных функций

 

Если результат трансляции программы из листинга 2.2.3 будет иметь имя prim223.exe и его запустить в виде командной строки

prim223.exe >kkk

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

Два...Четыре...Один...

Три

Это совсем не то, что можно было бы ожидать, глядя на последовательность операторов вывода в программе.

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

Практическая рекомендация совместного использования буферизованных и небуферизованных функций вывода в простейших случаях заключается в непременном использовании оператора fflush(stdout) за каждым оператором c функцией printf.

При программировании в Unix опустошение буфера при выводе на экран обеспечивается указанием управляющего символа конца строки. Если же выполняемая последовательность операторов задает вывод в буферизованный вывод экрана без управляющих символов '\n', то на экране этот текст появляется только после завершения всей программы! Альтернативой использованию указанных управляющих символов может быть также вызов описанных выше функций fflush(stdout).

При выводе же программой собственно в файл, для Unix характерны те же особенности, что уже рассмотрены на примере Windows. Это значит, что и в Unix при сочетании в одной программе функций небуферизованного вывода и буферизованных функций вывода следует использовать операторы fflush(stdout) для организации желаемой последовательности данных в файле результата. В простейших случаях следует каждый вывод через функцию printf сопровождать последующим оператором fflush(stdout).

 




Поделиться с друзьями:


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


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



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




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