Проблемы многострочного ввода при использовании STDIN.gets - PullRequest
3 голосов
/ 16 сентября 2010

Посмотрев на этот вопрос, у меня есть следующий код:

$/ = "\0"
answer = STDIN.gets

Теперь я надеялся, что это позволит пользователю:

  • введите многострочный ввод, завершив его нажатием Ctrl-D .
  • введите однострочный ввод, оканчивающийся нажатием Ctrl-D .
  • введите «ничего», заканчивая нажатием Ctrl-D .

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

  • Пользователь может ввести штраф в несколько строк.
  • Пользователь может не ввести однострочный ввод, , если не нажмет Ctrl-D дважды .
  • Пользователь может ввести «ничего», если он сразу нажмет Ctrl-D .

Итак, почему для однострочной ситуации (т. Е. Если пользователь ввел некоторый текст, но без новой строки, а затем нажал Ctrl-D ) требуется два нажатия Ctrl-D ? И почему тогда это работает, если пользователь ничего не вводит? (Я заметил, что если они ничего не вводят и нажимают Ctrl-D , я получаю не пустую строку, а класс nil - я обнаружил это при попытке вызвать .empty? для результата, так как вдруг ужасно потерпел неудачу. Если есть способ заставить его возвращать и пустую строку, это было бы неплохо. Я предпочитаю проверять .empty? на ==, и не особо хочу определять .empty? для ноля класс.)

РЕДАКТИРОВАТЬ : Поскольку я действительно хотел бы знать «правильный способ» сделать это в Ruby, я предлагаю вознаграждение в 200 повторений. Я также приму ответы, которые дают другой способ ввода терминального многострочного ввода с разумной процедурой «отправки» - я буду судить о «подходящем». Например, в настоящее время мы используем два символа "\ n", но это не подходит, поскольку оно блокирует абзацы и не интуитивно понятно.

Ответы [ 2 ]

2 голосов
/ 21 сентября 2010

Основная проблема - сам терминал. Смотрите многие ссылки по теме справа от вашего поста. Чтобы обойти это, вам нужно перевести терминал в исходное состояние. Следующее работало для меня на машине Solaris:

#!/usr/bin/env ruby
# store the old stty settings
old_stty = `stty -g`
# Set up the terminal in non-canonical mode input processing
# This causes the terminal to process one character at a time
system "stty -icanon min 1 time 0 -isig"
answer = ""
while true
  char = STDIN.getc
  break if char == ?\C-d # break on Ctrl-d
  answer += char.chr
end
system "stty #{old_stty}" # restore stty settings
answer

Я не уверен, нужно ли сохранять и восстанавливать настройки stty, но я видел, как это делают другие люди.

2 голосов
/ 16 сентября 2010

При чтении STDIN с оконечного устройства вы работаете в режиме, немного отличающемся от чтения STDIN из файла или канала.

При чтении из tty Control-D (EOF) действительно отправляет EOF, только есливходной буфер пуст.Если он не пустой, он возвращает данные в системный вызов read, но не отправляет EOF.

Решение состоит в том, чтобы использовать какой-либо ввод-вывод более низкого уровня и одновременно читать символ.Следующий код (или что-то подобное) будет делать то, что вы хотите

#!/usr/bin/env ruby

answer = ""
while true
  begin
    input = STDIN.sysread(1)
    answer += input
  rescue EOFError
    break
  end
end

puts "|#{answer.class}|#{answer}|"

Результаты выполнения этого кода с различными входами следующие: -

INPUT Thisэто линия

|String|This is a line
|

ВХОД Это строка

|String|This is a line|

ВХОД

|String||
...