Существует 2 ключевых подхода по взаимодействию между пользователем и приложением, когда вы разрабатывается приложение для командного интерфейса:

  • Интерактивное взаимодействие.
  • Выполнение команды по аргументам приложения.

Причём последний тип взаимодействия обладает рядом преимуществ:

  • Количество аргументов фиксировано, для каждого из них предоставляется описание по назначению и применению.
  • Для большинства из них предусмотрены значения по умолчанию, что уменьшает количество инормации, которую должен ввести пользователь.
  • Данные в аргументах можно передавать в рамках различных скриптов, которые могут выполняться автоматически в зависимости от наступления определённого события, например когда пользователь пришёл домой, на работу, пришло письмо по электронной почте и тому подобное.
  • Список аргументов обычно можно узнать до начала использования приложения.
  • Данные, обрабатываемые приложением, не надо подготавливать к изменению в течении длительного времени. Можно реализовать простые и понятные схемы обработки данных.

Однако интерактивное взаимодействие тоже следует применять, ввиду того, что:

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

В языке Ruby и его стандартной библиотеке предусмотрены широкие возможности по реализации этих сценариев. На этом занятии мы познакомимся с инструментами, необходимыми для разработки приложений в интерактивном режиме, а тажке рассмотрим базовые подходы к обработке аргументов командного интерфейса. Расширенные подходы к работе с аргументами рассмотрим на следующих занятиях.

Работа в интерактивном режиме

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

Вывод на стандартный поток

Для вывода информации на стандартный поток вывода Ruby предоставляет следующие методы:

  • Kernel#puts - позволяет вывести строку текста на стандартный поток вывода. Строка завершается символом перевода на новую строку.
  • Kernel#print - позволяет вывести строку текста на стандартный поток вывода, после строки не печатается символ перехода на новую строку. Подходит для формирования приветствий.
  • Kernel#printf - позволяет вывести информацию согласно согласно форматированной строке. Может выводить информацию как на стандартный поток вывода, так и на произвольный поток вывода. Описание формата приведено в документации на метод Kernel#format
  • Kernel#p - позволяет вывести информацию о переданных объектах в формате, показывающем структуру объекта с точки зрения зыка программирования Ruby. Очень удобно использовать метод для отладки приложения.
  • [pp] - позволяет вывести информацию о данных, как и метод kernel#p, однако значительно лучше справляется с выводом информации о сложных структурах данных.
  • Kernel#putc - выводит на стандартный поток вывода один символ. В качестве параметра передаётся код данного символа.

Все указанные методы, за исключением pp, также доступны и для потоков вывода, которые унаследованы от класса 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.

Практикум

Приложение-повторятель

Реализуйте приложение, которое будет повторять предложения, введённые пользователем. Приложение должно выполнять следующие действия:

  1. Сразу после запуска печатать информацию о тех действиях, которые приложение позволяет выполнять, о тех данных, которые ожидаются от пользователя.
  2. Перед вводом пользователя обязательно показывать приглашение для ввода данных. Приглашение может быть простым, например > , или более информативным, например введите строку> .
  3. Считайте строку, которую ввёл пользователь и выведете её на экран с помощью методов puts, print, printf, p
  4. Предложите пользователю ещё раз ввести данные.
  5. Завершите считывание данных, когда пользователь введёт строку “stop, please”.

Простейший калькулятор, умеющий складывать

Реализуйте приложение, которое будет считывать от пользователя вещественные числа и складывать их с предыдущими введёнными числами.

  1. Приложение должно показывать справку об использовании, а также приглашение для ввода данных.
  2. Когда пользователь ввёл строку, приложение должно считать её, преобразовать к вещественному числу, прибавить к текущей сумме и вывести:
    • Значение считанного числа.
    • Значение суммы на настоящий момент.
  3. Бесконечный цикл считывания данных должен закончиться, если
    • Пользователь ввёл строку “over”
    • Пользователь завершил поток ввода данных, введя сочетание клавиш Ctrl+D.
  4. По окончании работы приложение должно вывести результат сложения чисел.

Реализация корректного ввода положительных вещественных чисел

Реализуйте приложение, которое будет контролировать, что пользователь ввёл целое положительное число.

  1. Приложение должно показывать справку об использовании, а также приглашение для ввода данных.
  2. Приложение должно считывать строку, введённую пользователем, преобразовывать её к вещественному числу.
    • Если пользователь ввёл отрицательное число или 0, то сообщать пользователю об ошибочном вводе.
    • Если пользователь ввёл положительное число, то сообщать ему о правильном вводе и выводить введённое число.
    • Дополнительно в последнем случае проверять, что введённая строка не содержит других символов кроме введённого числа.
  3. Бесконечный цикл заканчивается, если
    • Пользователь ввёл число “99.999”
    • Пользователь завершил поток ввода данных, введя сочетание клавиш Ctrl+D
  4. По окончании работы пиложение

Решение задачи №3 из пункта 1.2 задачника

Реализуйте задачу №3 из пункта 1.2 задачника. При этом выполните следующие условия

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