なんだかモゾモゾとした Go への興味を持ちつつ、Scala でコレクション操作の気持ちよさを覚えたり、nodejs の http リクエストの簡単さに驚きと喜びを覚えてしまったが故に、コレクション操作ゼロ・http リクエスト一つ飛ばすのに結構な行数を必要とする Go を勉強するのがなんだか億劫になっている今日この頃。
でも頑張って勉強中
そんでもって今回は標準入力を使ってデータを読み込んで、よしなに処理して、標準出力に出力
といったプログラムを組みたかったので、折角だし Go でトライしてみた
まずは os パッケージを調べた
まずはドキュメントを調べよう。
確か Go には os って名前のパッケージがあったはず。。。。
標準入出力っていったらやっぱ os パッケージでしょー
ってことで Golang os
で検索。
公式ドキュメントを無事発見して stdin でページ検索。
お、やっぱあった
でもどうやって入力読み込むのこれ?
os.Stdin
はパッケージの中で Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
って定義されているそうな。
って事はこれは File タイプなので、 File インターフェースに紐付いたメソッドを確認。
fmfm、func (f *File) Read(b []byte) (n int, err error)
とな。
とりあえず試す。
package main
import (
"os"
"fmt"
)
func main() {
buf := []byte{}
n, _ := os.Stdin.Read(buf)
fmt.Println("Read", n, "lines")
fmt.Println("Buf", string(buf))
}
$ echo "hello world!" | go run stdin.go
Read 0 lines
Buf
。。。ん?
$ echo "hello?" | go run stdin.go
Read 0 lines
Buf
あ、違う...の?
Golang stdin
で調べてみた
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
stdin := bufio.NewScanner(os.Stdin)
for stdin.Scan() {
if err := stdin.Err(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
fmt.Println(stdin.Text())
}
}
公式ドキュメントを覗いてみる と定義は
func NewScanner(r io.Reader) *Scanner
となっている
。。。ん?
io.Reader?
でも渡しているのは os.Stdin
で、これは File
タイプ。
そして Go に継承はないらしい。。。
どういうこっちゃ?
インターフェースの便利さを(なんとなく)知る
io.Reader の定義を覗きに行くと次のような定義がある。
type Reader interface {
Read(p []byte) (n int, err error)
}
ん?これさっき見た?
もっかい File タイプを調べるとやっぱりあった。
func (f *File) Read(b []byte) (n int, err error)
あ〜、同じメソッドが定義されてるから、定義としてなくても、io.Reader と互換性があるって事で使えるのか
自分が活用できるかはさておき納得
ちなみに (s * Scanner).Scan()
は毎行読み込むのに必要
(s *Scanner).Text()
は、今のバッファ内文字列を返すだけで、バッファの内容を次の行に更新するには (s *Scanner).Scan()
のコールが必須。
逆に言うと (s *Scanner).Text()
は必ず同じ値を返すので、変数に格納する事なくガンガン呼べる。(ある程度回数がある場合は利便性のために変数に格納した方が良いとは思うけど)(あとパフォーマンス面ではどうなるのか知らない)