i21sys/Docu.Полезные сведения/Ввод данных (In).odc

Ввод данных в программу с помощью средств модуля i21sysIn

В Блэкбоксе (как и во всех Оберонах) роль командной строки может сыграть любой текст. Эта идея известна под названием тексткакинтерфейс.
Мы уже знаем, что команду можно вызвать из любого текста, где видно её имя (с помощью коммандера , приставленного непосредственно перед именем команды, или через меню: Dev, Execute, если предварительно имя выделить мышкой).
Но входные данные тоже можно брать из текстовых документов. А именно:
1) Набор вводимых данных  входной поток  может быть частью любого текста. Для одной и той же программы может быть приготовлено несколько таких наборов в разных текстовых документах.
2) Есть способы указать, какой именно фрагмент и в каком именно тексте будет считаться входным потоком, начиная с данного момента.
3) Программа использует некоторые стандартные способы получения доступа ко входному потоку и его чтения.

Эта идея реализована в модуле i21sysIn, имеющемся во всех комплектах Блэкбокса от проекта Информатика-21.
Более ограниченный вариант  модуль In, описанный в старой книге Вирта и Райзера по программированию на Обероне и включенный в качестве примера в дистрибуцию Блэкбокса. Все программы, использующие модуль In, будут корректно работать и с модулем i21sysIn, для этого в списке импорта достаточно вместо In указать In := i21sysIn.
Ниже даны некоторые пояснения, а полное описание возможностей модуля i21sysIn дано в его документации.
(В учебных комплектах, ориентированных на школу, имеется сокращённый вариант этого модуля  i21eduВвод, в котором все видимые имена переведены на русский язык.) 

В дальнейшем будем предполагать, что список импорта (подключения внешних библиотек) содержит In := i21sysIn (в школьном варианте Ввод := i21eduВвод).

Простейший пример ввода пары чисел для простейшей программы турбопаскалевского типа показан здесь. Там реализован один из описываемых ниже способов определения входного потока. 
Модуль i21sysIn предоставляет несколько таких способов.

Первый способ  самый простой и удобный, например, для школьных уроков. Здесь поток ввода определяется вручную до выполнения программы, и его не надо повторно определять при повторном запуске программы: 
 мышкой выделяется нужный фрагмент текста (в любом открытом документе); 
 нажимается Shift+F1 (или мышкой через меню: 1-Школьное, Открыть поток ввода или Текст, Открыть поток ввода или Text, Open Input).
После этого окно с потоком ввода можно даже закрыть.
Программа в этом случае использует команду In.Restart (в школьном варианте Ввод.Сначала), чтобы начать чтение потока с начала, а также обычные команды чтения вроде In.Int(...) (в школьном варианте Ввод.Цел(...)).
Программа может возвращаться к началу потока ввода столько раз, сколько нужно.
Достоинство этого способа  полное разделение выполнения программы и процедуры задания входного потока.

Во втором способе входной поток должен быть выделен мышкой на момент вызова программы, а программа должна использовать команду In.OpenSelection, и если при выполнении последней выделенного фрагмента не обнаруживается, то логическая переменная In.done (Ввод.сделано) устанавливается в FALSE. 
Это способ удобен, когда нужно много раз выполнять программу с разными данными на входе.

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

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

Последний способ: программа открывает входной поток, вызывая процедуру In.OpenFocus, и тогда входным потоком будет весь текстовый документ в переднем окне. Этот способ имеет смысл только для программ, вызываемых через меню.

Программный способ: можно программно заставить этот модуль читать данные из любого фрагмента любого объекта типа TextModels.Model, открыв поток ввода вызовом процедуры In.OpenText. См. подробности в документации. Отметим только, что открытие потока ввода и его обработка могут быть реализованы в виде разных процедур и даже в разных модулях.

Некоторые примеры даны здесь.

Нужно обратить внимание на следующее:

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

 После каждой операции ввода нужно проверять, что она завершилась успешно, с помощью проверки ASSERT( In.done ) 
или в школьном варианте УБЕДИТЬСЯ( Ввод.сделано );
Проверка корректности ввода должна быть автоматической привычкой.

 Считываемые значения могут разделяться любыми белыми литерами в любом количестве и любых комбинациях (пробелы, концы строк, табуляции, ...). 
Например, вводимые данные можно для наглядности группировать по строкам или как-то форматировать с помощью табуляции. 
Если значения разделяются, например, запятыми, то для пропуска запятых можно использовать процедуру In.Char.

 Типы вводимых значений во входном потоке должны совпадать с тем, что ожидают в данный момент команды чтения (целые для In.Int и т.д.). 
В противном случае In.done будет установливаться в FALSE, а в дальнейшем будут происходить аварийные остановки (потому что ввод нужно проверять). 
После такой аварийной остановки достаточно закрыть открывшееся окошко TRAP/ТРАП, исправить программу или данные, и повторить попытку.
Важное исключение составляет процедура чтения In.Char, которая просто берёт из входного потока очередную литеру.
Другое исключение  процедура In.Real, которая правильно прочтёт не только вещественное значение (с десятичной точкой), но и целое, и преобразует его в вещественное. Этот случай безопасен в том смысле, что не влечёт потери информации, поэтому он разрешён.

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

 Если вводится литерная цепочка (процедура In.String), то она должна либо быть заключена в кавычки, либо не содержать белых литер.

 Все процедуры, кроме In.Char, пропускают белые литеры до следующего элемента данных. In.Char этого не делает. В частности, если программа вызывается с помощью коммандера, поток ввода открывается с помощью In.Open, и первой операцией чтения стоит In.Char, то она прочтёт белую литеру, которая ограничивает имя команды. Например, если команда и поток ввода оформлены так:
Module.Procedure x
то прочтен будет пробел, а не "x".
Аналогично вызов In.Char сразу после успешного чтения целого числа посредством In.Int прочтет первую белую литеру сразу за последней цифрой числа.

 Модуль i21sysIn экспортирует сканер i21sysIn.scanner, имеющий тип TextMappers.Scanner, который подключён к текущему входному потоку. Это позволяет сканировать входной поток, используя весь арсенал средств сканера (определять атрибуты текста и т.п.).

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

________________
info21@inr.ac.ru
последние изменения: 2012-09-24