演習6

演習5(3)のプログラムを実行する際,sttyコマンドを打込まなくていいように,プログラムを変更せよ.(ヒント:os/execパッケージのRun関数を用いると,プログラムから外部コマンドを実行することができる)

解答例

  1. package main
  2. import (
  3. "time"
  4. "bufio"
  5. "os"
  6. "os/exec"
  7. "./textbox"
  8. )
  9. const text = "Go言語は、Googleが開発した新しいプログラミング言語です。"
  10. // --- ProblemBox ---
  11. // 問題文を0.5秒ごとに1文字ずつ表示するデータ型.
  12. type ProblemBox struct {
  13. *textbox.TextBox
  14. text []rune
  15. }
  16. // 位置,大きさ,表示する問題文を指定してProblemBoxを生成する.
  17. func New(x,y,w,h int, s string) (pb *ProblemBox) {
  18. return &ProblemBox{textbox.New(x,y,w,h),[]rune(s)}
  19. }
  20. // 問題文を0.5秒ごとに表示する.
  21. func (pb *ProblemBox) Play() {
  22. pb.Clear()
  23. for i:= 0; i < len(pb.text); i++ {
  24. pb.PutRune(pb.text[i])
  25. time.Sleep(5e8)
  26. }
  27. pb.Puts("[Enter]")
  28. }
  29. // --- ProblemBox end ---
  30. func main() {
  31. cmd := exec.Command("stty")
  32. cmd.Args = []string {"stty", "-echo", "cbreak"}
  33. cmd.Stdin = os.Stdin
  34. cmd.Run()
  35. defer func() {
  36. cmd = exec.Command("stty")
  37. cmd.Args = []string {"stty", "sane", "erase", "^H"}
  38. cmd.Stdin = os.Stdin
  39. cmd.Run()
  40. }()
  41. textbox.Clear()
  42. textslice := []rune(text)
  43. t1 := New(3,3,70,4, text)
  44. go t1.Play()
  45. t2 := textbox.New(3,8,70,4)
  46. t3 := textbox.New(3,13,70,4)
  47. keyin := bufio.NewReader(os.Stdin)
  48. for i:=0; i < len(textslice)+1; i++ {
  49. c,_,_:= keyin.ReadRune()
  50. if c == '\n' {
  51. t2.Puts("[Enter]")
  52. } else {
  53. t2.PutRune(c)
  54. }
  55. if c == '\n' && i == len(textslice) {
  56. t3.Puts("おめでとう! 成功です.")
  57. break
  58. } else if i < len(textslice) && c != textslice[i] || c != '\n' && i == len(textslice) {
  59. // 途中で間違えた || 最後に[Enter]以外の文字を押した
  60. t3.Puts("残念でした.キーが違います.")
  61. break
  62. }
  63. }
  64. }

解説

os/execパッケージ

  • os/execパッケージは,外部コマンドを実行するためのパッケージです.os.StartProcessをラップして,stdin,stdoutの再マップなどを容易にしています.
  • 外部コマンドを実行するには,解答例の38行目から41行目のようにプログラムします.Command関数でCmd型のデータを生成し,そのフィールドに値をセットします.ArgsにコマンドとパラメータをセットしてRun()メソッドを呼び出すことで,そのコマンドを実行します.
  • os/execパッケージの詳細は,Package exec(英語)を参照してください.

defer文

  • defer文は,defer文を記述している関数自体がリターンするまで,指定した関数の実行を先延ばしします.
  • 解答例の42行目から47行目の関数リテラルは,main()関数がリターンする直前(すなわち,プログラムの終了直前)に実行されます.
  • 関数内のどの経路を通ってリターンするかに関わらず,ファイルのクローズなど,開放しなければならないリソースなどを処理するのに有効です.
  • defer文の詳細については,言語仕様実践Go言語を参照してください.

プログラムの動作(main)

  • 38行目~41行目:コマンド stty cbreak -echo を実行.
  • 42行目~47行目:コマンド stty sane erase ^H をmain関数の最後に実行.
  • 49行目以降:演習5と同じ動作