Взаимодействие с пользователем в формате командного интерфейса
Существует 2 ключевых подхода по взаимодействию между пользователем и приложением, когда вы разрабатывается приложение для командного интерфейса:
- Интерактивное взаимодействие.
- Выполнение команды по аргументам приложения.
Причём последний тип взаимодейтсвия обладает рядом преимуществ:
- Количество аргументов фиксировано, для каждого из них предоставляется описание по назначению и применению.
- Для большинства из них предусмотрены значения по умолчанию, что уменьшает количество инормации, которую должен ввести пользователь.
- Данные в аргументах можно передавать в рамках различных скриптов, которые могут выполняться автоматически в зависимости от наступления определённого события, например когда пользователь пришёл домой, на работу, пришло письмо по электронной почте и тому подобное.
- Список аргументов обычно можно узнать до начала использования приложения.
Однако интерактивное взаимодействие тоже следует применять, ввиду того, что:
- Интерактивное взаимодействие зачастую проще, так как не требует от пользователя сначала найти все опции, а затем ввести необходимые данные.
- В интерактивном режиме приложение может предоставлять контекстные подсказки, т.е. подсказки на основании действий пользователя, делая их более информативными.
- В интерактивном режиме пользователь может ввести большое количество информации, причём её проверка будет осуществляться на этапе ввода, а не на этапе использования, уберегая пользователя от повторного ввода большого объёма данных.
- Трудно играть в режиме обработки переданных аргументов.
В языке Ruby и его стандартной библиотеке предусмотрены широкие возможности по реализации этих сценариев. На этом занятии мы познакомимся с инструментами, необходимыми для разработки приложений в интерактивном режиме, а тажке рассмотрим базовые подходы к обработке аргументов командного интерфейса. Расширенные подходы к работе с аргументами рассмотрим на следующих занятиях.
Работа в интерактивном режиме
При работе в интерактивном режиме у вас всегда есть 2 потока информации, с которыми необходимо работать: поток стандартного ввода, поток стандартного вывода. Помимо них есть ещё стандартный поток вывода сообщений об ошибке. На последний поток зачастую выводят расширеныне сообщения об источнике проблемы и другую отладочную информацию.
Вывод на стандартный поток
Для вывода информации на стандартный поток вывода Ruby предоставляет следующие методы:
- Kernel#puts - позволяет вывести строку текста на стандартный поток вывода. Строка завершается символом перевода на новую строку.
- Kernel#print - позволяет вывести строку текста на стандартный поток вывода, после строки не печатается символ перехода на новую строку. Подходит для формирования приветствий.
- Kernel#printf - позволяет вывести информацию согласно согласно форматированной строке. Может выводить информацию как на стандартный поток вывода, так и на произвольный поток вывода. Описание формата приведено в документации на метод Kernel#format
- Kernel#p - позволяет вывести информацию о переданных объектах в формате, показывающем структуру объекта с точки зрения зыка программирования Ruby. Очень удобно использовать метод для отладки приложения.
- Kernel#putc - выводит на стандартный поток вывода один символ. В качестве параметра передаётся код данного символа.
Все вышеобозначенные методы также доступны и для потоков вывода, которые унаследованы от класса IO. Например, IO#puts - позволяет вывести строку на любом потоке вывода информации. В частности, вам доступна глобальная переменная $stdout
, которая связана со стандартным потоком вывода информации.
Ввод со стандартного потока
Для получения информации со стандартного потока ввода Ruby предоставляет следующие методы:
- Kernel#gets - считывает одну строку со стандартного ввода. Считываение строки начинается, когда пользователь нажимает клавишу
на клавиатуре. Когда поток заканчивается возвращает `nil`. Вы можеет послать сигнал завершения потока, нажав сочетание клавиш `Ctrl+D`. *Внимание*. Метод начинает работать по-другому, если приложению в качестве аргументов были переданы пути к файлам. - Kernel#readline - работает также как и
gets
за исключением того, что при окончании потока ввода выбрасывает исключение EOFError - Kernel#readlines - считывает все линии, которые были переданы на стандартный поток ввода, в массив. Считывается продолжается до окончания потока ввода.
Стоит отметить, что эти методы начинают работать по-другому, когда в качестве аргумента приложению передаются файлы. В этом случае считывание данных начинается из переданных файлов. К сожалению данная практика сильно отличается от других языков программирования и может привести к непредсказуемым последствиям в работе приложения. Ввиду этого рекомендуется использовать альтернативный подход с явным указанием источника получения информации.
Объект $stdin
В переменную $stdin
записана ссылка на объект, взаимодействующий со стандартным потоком ввода. Эта ссылка в отличие от предыдущих методов не изменяет своего поведения. В неё записан объект класса IO. Рассмотрим его методы по получению информации.
- IO#gets - получить строку целиком с потока ввода.
- IO#getc - получить 1 символ с потока ввода в виде строки. Когда поток заканчивается, метод возвращает
nil
. - IO#getbyte - получить 1 символ с потока ввода в виде кода числа.
- IO#read - считать определённое количество символов, по умолчанию все символы с потока ввода.
- IO#readline - считать 1 строку, при окончании потока выбрасывает исключение
EOFError
. - IO#readlines - считать строки с потока ввода и поместить их в массив.
Обработка строк
Преобразование к числам
Для решения многих задач вам потребуется работать не со строками, а с другими примитивами, в частности числами. Рассмотрим методы, которые доступны для преобразования строк в число.
- Kernel#Integer - преобразует переданный ему аргумент в целое число. В качестве аргумента может выступать не только строка, но также и вещественное число.
- Kernel#Float - преобразует переданный аргумент в вещественное число.
Данные методы ожидают, что им будет передан корректный аргумент. Если в качестве аргумента передаются некорректные данные, тогда данные методы будут выбрасывать исключение о неправильном формате данных ArgumentError. Таким образом можно организовать надёжную проверку данных, однако мы с вами пока что не умеем обрабатывать исключительные ситуации, поэтому посмотрим на другие методы, предоставляемые классом String.
- String#to_i - преобразовать текущую строку к целому числу и вернуть данное число. Данный метод постарается найти целое число в начале строки и преобразовать его к числу. Если число не было найдено, то метод вернёт число
0
в качестве результата. - String#to_f - преобразовать текущую строку к вещественному числу и вернуть получившееся число. Метод страдает от тех же недостатков, что и предыдущий.
Таким образом последние методы нельзя использовать для надёжной проверки ввода, однако они достаточно просты и их можно использовать в тех ситуациях, когда вы уверены в том, что строки действительно содержат необходимые данные и их обработка не приведёт к неожиданным последствиям.
Отбрасывание лишнего у строк
Рассмотренный ранее метод gets
считывает строку целиком, включая пробелы в начале и конце строки, а также символ переноса строки, который находится в самом конце. Для эффективной обработки строк эти символы необходимо удалить, так как они не несут в себе полезной информации. Стандартный класс String предоставляет огромное количество методов, которые полезны в решении различных задач. Мы не будем рассматривать их все сейчас, выделим только наиболее важные для текущих задач.
- String#strip - метод удаляет все пустые символы в начале и в конце строки, включая переносы строк. В результате текущая строка не изменяется, но а создаётся новая строка с нужным содержимым. Не забудьте сохранить ссылку на новую строку. У этого метода также есть 2 специализированные версии:
- String#lstrip - удалить все пробелы с начала строки.
- String#rstrip - удалить все пробелы с конца строки.
- String#chomp - метод удаляет перенос каретки в конце строки. Этот метод следует использовать, когда вас моут интересовать пробелы в начале и в конце строки, но вы хотите избавиться от «паразитного» переноса, который возникает при использовании метода
gets
.
Практикум
Приложение-повторятель
Реализуйте приложение, которое будет повторять предложения, введённые пользователем. Приложение должно выполнять следующие действия:
- Сразу после запуска печатать информацию о тех действиях, которые приложение позволяет выполнять, о тех данных, которые ожидаются от пользователя.
- Перед вводом пользователя обязательно показывать приглашение для ввода данных. Приглашение может быть простым, например
>
, или более информативным, напримервведите строку>
. - Считайте строку, которую ввёл пользователь и выведете её на экран с помощью методов
puts
,print
,printf
,p
- Предложите пользователю ещё раз ввести данные.
- Завершите считывание данных, когда пользователь введёт строку “stop, please”.
Простейший калькулятор, умеющий складывать
Реализуйте приложение, которое будет считывать от пользователя вещественные числа и складывать их с предыдущими введёнными числами.
- Приложение должно показывать справку об использовании, а также приглашение для ввода данных.
- Когда пользователь ввёл строку, приложение должно считать её, преобразовать к вещественному числу, прибавить к текущей сумме и вывести:
- Значение считанного числа.
- Значение суммы на настоящий момент.
- Бесконечный цикл считывания данных должен закончиться, если
- Пользователь ввёл строку “over”
- Пользователь завершил поток ввода данных, введя сочетание клавиш
Ctrl+D
.
- По окончании работы приложение должно вывести результат сложения чисел.
Реализация корректного ввода положительных вещественных чисел
Реализуйте приложение, которое будет контролировать, что пользователь ввёл целое положительное число.
- Приложение должно показывать справку об использовании, а также приглашение для ввода данных.
- Приложение должно считывать строку, введённую пользователем, преобразовывать её к вещественному числу.
- Если пользователь ввёл отрицательное число или 0, то сообщать пользователю об ошибочном вводе.
- Если пользователь ввёл положительное число, то сообщать ему о правильном вводе и выводить введённое число.
- Дополнительно в последнем случае проверять, что введённая строка не содержит других символов кроме введённого числа.
- Бесконечный цикл заканчивается, если
- Пользователь ввёл число “99.999”
- Пользователь завершил поток ввода данных, введя сочетание клавиш
Ctrl+D
- По окончании работы приложение
Решение задачи №3 из пункта 1.2 задачника
Реализуйте задачу №3 из пункта 1.2 задачника. При этом выполните следующие условия
- Приложение должно выводить справку об использовании и использовать приглашения для ввода данных.
- Приложение должно проверять корректность ввода данных. В случае если данные не верны, то приложение должно сообщать об этом пользователю и просить его повторить ввод.
- Результат работы приложения должен быть представлен в доступной для обычного человека форме.