Классификация ввода-вывода
Операторы ввода-вывода данных являются наиболее важными операторами любого языка программирования. Без них невозможно никакое общение между ЭВМ, и между ЭВМ и человеком. Однако в реализации этих операторов существуют большие различия, в зависимости от устройства вывода, технологии вывода и т.п. Классификацию операторов ввода-вывода смотри ниже.
1.1. Классификация по способу вывода
По способу ввода-вывода информации на периферийное устройство используют следующую классификацию:
- Прямое программирование устройств. При этом способе программа сама, без помощи других программ, программирует периферийное устройство. Хотя этот способ является исторически первым и обеспечивает максимальное быстродействие, в настоящее время он не используется из-за своей практической "непереносимости" между компьютерами.
- Прямое программирование через драйвер устройства. Драйвер - это такая программа, которая "перехватывает обращение" пользователя или операционной системы к периферийному устройству, предлагая более или менее "унифицированный интерфейс" функций для работы с устройством. При этом пользователю не нужно "вникать" в аппаратную реализацию устройства, обращаться к регистрам и портам устройства по уникальным адресам, и, в конце концов, держать всю эту информацию в своей голове. Драйвер предоставляет доступ к функциям управления устройствами либо через "прерывания" (в MS-DOS), либо через специальные функции операционной системы (Windows, Unix), либо как некоторый объект с методами (Win32 и др.). Недостаток этого метода заключается в том, что написать один драйвер с интерфейсом "на все случаи жизни" не представляется возможным. Поэтому программирование через драйвер устройства является также "сложной работой" даже для профессионалов, однако такого рода ввод-вывод является: "вводом-выводом среднего уровня". На практике используется "ввод-вывод высокого уровня", когда с драйвером устройства будет работать одна или несколько "промежуточных программ", обеспечивающих единый интерфейс ввода-вывода для прикладных программ.
- Буферизированный (потоковый) ввод-вывод. Этот вывод реализован на уровне консоли в MS-DOS, Windows и UNIX. При этом способе в оперативной памяти ЭВМ создаётся "буфер" для записи или считывания из него символов, и непосредственным их вводом-выводом на устройство занимается операционная система.
Недостатки буферизированного ввода-вывода следующие:
- при таком вводе-выводе невозможно задать шрифтовое и абзацное оформление текста - используется только "поток" символов;
- такой ввод-вывод ограничен консолью и консольными операциями перенаправления вывода. С его помощью нельзя реализовать WIMP, SILK и другие интерфейсы;
- редактировать такой поток можно только с помощью "внешних программ-редакторов".
Преимуществом же потокового способа ввода-вывода является возможность "гибко перенаправлять потоки" с устройства на устройство из числа тех, которые поддерживает операционная система.
- Ввод-вывод с использованием API. Термин "API" расшифровывается как "Application Programming Interface" - "интерфейс программирования приложений". С помощью этого интерфейса можно создавать программы "на высоком уровне абстракции" от реальной конфигурации ЭВМ и периферийных устройств. Программа описывает свои действия на языке "в общем виде", все детали формирования изображения и ввода-вывода от него "скрыты". Программист может задавать шрифтовое и абзацное оформление, выводить графику вместе с текстом - всё это будет реализовано одинаково на любом устройстве, которое поддерживает данное API. Недостатки такого вывода следующие:
- больший, по сравнению с буферизированным вводом-выводом, размер кода и количество подготовительных операций перед выводом;
- привязка ввода-вывода к одному API, а значит - к определённой платформе ЭВМ, библиотекам и операционным системам;
- невозможность "оперативного", без помощи программиста, перенаправления вывода.
Этими способами реализации ввода-вывода в основном ограничиваются операции ввода-вывода в разных языках программирования и операционных системах.
1.2. Классификация по обработке перед операцией ввода-вывода
По способу обработки информации при её вводе-выводе различают "форматированный" и "неформатированный" ("бинарный") ввод-вывод.
При форматированном вводе проводятся следующие операции:
- Задаётся ограничение на размер строки символов, читаемых с устройства;
- Числовые данные при вводе преобразуются в "двоичные" в соответствии с их форматом;
- При чтении чисел проверяется их формат, а именно:
- длину строки цифр;
- наличие и местоположение десятичной точки;
- наличие и значение символа "порядка" при чтении чисел в "научном формате";
- Целые числа, в зависимости от формата чтения, могут быть десятичными, восьмеричными, шестнадцатеричными, датой, временем и т.п. Всё это указывается в опциях формата, задаваемых в операциях ввода.
При форматированном выводе производятся операции, противоположные тем, что применялись при вводе, а именно:
- Данные из двоичной формы преобразуются в текстовую форму, в соответствии со строкой формата;
- Эти текстовые данные "обрезаются" до определённой длины, а также осуществляется "выравнивание" текстовой информации в выводимой строке.
При "неформатированном текстовом" выводе данные переводятся в текстовый формат, однако при этом не задаётся ни ограничение на длину, ни на "выравнивание" символов, ни на положение десятичной точки. Читать такие данные неудобно, а тем более - передавать их обратно машине.
Существует также "неформатированный двоичный" ввод-вывод, когда данные передаются на устройство или считываются с него "как есть", безо всяких преобразований. Такие файлы являются, как правило, двоичными ("бинарными").
Кроме того, следует отметить, что записи данных, которые передаются устройству ввода-вывода "за один приём", могут иметь фиксированную или переменную длину (всё равно, являются они форматированными или нет). Тогда к файлу, запись которого имеет фиксированную длину, можно организовать "прямой доступ", т.е. любую его запись можно "прочитать", зная начало файла и номер "смещение" записи в файле. Это очень удобно при организации баз данных.
1.3. Замечания по использованию ввода-вывода при помощи API
При использовании ввода-вывода при помощи API возможны следующие нештатные ситуации:
- ошибка при обращении к файлу, открытым в одной части программы, и закрытым в другой её части;
- разные программы хотят получить одни и те же ресурсы в "монопольное использование" (так называемые "dead lock", "смертельные объятия");
- ошибка при выводе на устройство, контекст которого не был создан или уже закрыт в другой части программы.
Все эти ситуации приводят к "зависанию" компьютера. А причиной этих ситуаций, не возникающих в "однопользовательском режиме MS-DOS", является то, что порядок выполнения частей программ, обращения к устройствам, многозадачность, наконец, определяется не "забитой в алгоритм последовательности действий", а поступившим на обработку приложения системным сообщениям. В этом случае сообщения поступают, вообще-то, в произвольном порядке, а обработчик должен обрабатывать каждое сообщение единообразным способом.
Вот почему обязательно следующее правило:
"Операция обращения к устройству, открытия, чтения, записи и закрытия файлов, создания и уничтожения контекста устройства и вывод на него должны осуществляться в пределах обработки одного сообщения из очереди сообщений" (например, сообщений Windows).
Если Вы, например, открыли файл в обработчике одного сообщения, закрыли при обработчике другого сообщения, а обратились к нему при обработке третьего сообщения, то Ваша программа рано или поздно "зависнет", и виноваты в этом будете только Вы!
Также для предупреждения "смертельных объятий" при обращении к базам данных правильно используйте вызовы функций СУБД. Если Вам отказано в доступе к файлу базы данных, не пытайтесь "обойти" этот запрет!
1.4. Резюме
Итак, в данном разделе Вы познакомились с основными классификациями ввода-вывода ЭВМ. Вы узнали, что эта классификация может идти по "способу абстрагирования" программирования ввода-вывода от конкретной реализации физических устройств, от того, преобразует ли программа данные, полученные от устройства, и какова "глубина этой обработки". При этом мы оперируем понятием "периферийное устройство ЭВМ" а не, "клавиатура Genius", "принтер Hewlett-Packard LaserJet 5100" и т.п. На данном уровне изложения это не важно.
Самым важным пунктом "Введение" является пункт 1.3. Могу спорить, что Вы вернётесь к этому пункту ещё не раз, пытаясь выправить такие ошибки, как "обращение к "закрытому" для чтения-записи периферийному устройству".
Теперь можете переходить к другим лекциям данной части курса. Все они носят справочный характер, необходимо только "уловить принцип" работы каждого из способов ввода-вывода.
1.5. Приложение I. Описание языков символьного программирования.
1.5.1. Язык Java
- Язык Java является императивным, объектно-ориентированным языком с байт-кодом, команды которого выполняются в командной строке либо в окне браузера Интернета [26];
- Текст программы на языке Java имеет имя: "имя_пакета".java - с расширением *.java. Скомпилированный байт-код имеет расширение: *.class ;
- Комментарии на языке Java такие же, как и в C++: пара скобок "/* … */" и "//";
- Переменные на языке Java требуют объявления и описания в начале функции, их использующей;
- Для строковых переменных существует отдельный класс: "String". Строки-литералы заключаются в двойные кавычки;
- В Яве любой файл-класс должен иметь заголовок:
public class "имя класса" { … Функции класса … }
Листинг 1.1. - Выполняемая функция (аналог функции 'main' на языке Си) имеет следующий шаблон:
public static (int|void) main( String[] args ) { … тело функции … }
Листинг 1.2. - Блоки операторов, как и в языке Си, в Яве выделяются фигурными скобками;
- Класс может содержать значения класса и методы класса. Вызываются они следующим образом: "переменная класса"."имя поля" или "переменная класса"."метод"("аргументы");
- Переменные класса создаются, объявляются и определяются внутри других классов. При написании определённого метода в качестве имени вызывающего объекта можно использовать зарезервированное слово this ;
- Символ "=" в Яве используется как оператор присваивания;
В качестве иллюстрации приведём программу.
Пример 1.1.
// Файл ex01001.java public class ex01001 { public static void main( String[] args ) { char cAnswer; String junk; System.out.println( "Hello, World!" ); System.out.print( "Не желаете ли поговорить? "); System.out.println( "Введите \'y\' или \'Y\' для Да или другую клавишу для Нет."); cAnswer = SavitchIn.readLineNonwhiteChar(); if( cAnswer == 'y' || cAnswer == 'Y' ) System.out.println( "Прекрасная погода, не так ли?" ); System.out.println( "До свидания!" ); System.out.println( "Press key Enter to exit..." ); junk = SavitchIn.readLine(); } }
Примечание: для ввода значений в программу используется класс "SavitchIn", текст которого находится в приложении с исходными текстами программы для данного курса.