3


0

STDIN.getsを使用する場合の複数行入力の問題

thisの質問を見て、次のコードがあります。

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

これで、ユーザーが次のことができるようになることを望んでいました。

  • 複数行の入力を入力し、[。kbd]#Ctrl-D#を押して終了します。

  • [.kbd]#Ctrl-D#を押して終了する単一行の入力を入力します。

  • [.kbd]#Ctrl-D#を押して終了する「何もしない」入力を入力します。

しかし、実際に私が見る動作は次のとおりです。

  • ユーザーは複数行の入力を細かく入力できます。

  • ユーザーは、ヒットしない_unless_の単一行入力を*入力*できません Ctrl-D twice.

  • ユーザーは、[。kbd]#Ctrl-D#を押すと「何もしない」入力を入力できます。 すぐに。

それで、なぜ単一行の状況(つまり ユーザーがテキストを入力し、改行は入力せずに[.kbd]#Ctrl-D#を押した場合、[。kbd]#Ctrl-D#を2回押す必要がありますか? そして、ユーザーが何も入力しないとなぜ機能するのですか? (何も入力せずにヒットすると Ctrl-D, I don’t get an empty string but the nil class - I 結果で `.empty?`を呼び出そうとしたときにこれが発見されました。突然ひどく失敗したためです 空の文字列を返すようにする方法があれば、それはいいでしょう。 「==」よりも「.empty?」をチェックすることを好み、nilクラスに対して「.empty?」を特に定義したくありません。)

編集:私は本当にRubyでこれを行うための「正しい方法」を知りたいので、私は200人の報奨金を提供しています。 また、賢明な「送信」手順で端末の複数行入力を入力する別の方法を提供する回答を受け入れます-私は「適切」の判断者になります。 たとえば、現在2つの「\ n」を使用していますが、段落をブロックし、直感的ではないため、これは適切ではありません。

2 Answer


2


端末デバイスからSTDINを読み取る場合、ファイルまたはパイプからSTDINを読み取るのとは少し異なるモードで作業しています。

tty Control-D(EOF)から読み取る場合、入力バッファーが空の場合にのみ実際にEOFを送信します。 空でない場合、読み取りシステムコールにデータを返しますが、EOFは送信しません。

解決策は、低レベルのIOを使用して、一度に1つの文字を読み取ることです。 次のコード(または同様のコード)があなたが望むことをします

#!/usr/bin/env ruby

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

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

このコードをさまざまな入力で実行した結果は次のとおりです。

*入力*これは行です

|String|This is a line
|

*入力*これは行です

|String|This is a line|

入力

|String||


2


基本的な問題は端末自体です。 投稿の右側にある関連リンクの多くをご覧ください。 これを回避するには、ターミナルを生の状態にする必要があります。 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設定の保存と復元が必要かどうかはわかりませんが、他の人がそれを行うのを見てきました。