入出力

入出力 #

標準入力から読み込む #

標準入力は os パッケージの変数 os.Stdin である。

これは os.File 型の値であるので、下記のファイル読み込み処理が使える。

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		panic(err)
	}
}

play_circleRun open_in_newRun In The Playground

参考ドキュメント: bufio , os

標準出力に書き込む #

標準出力は os パッケージの変数 os.Stdout である。

これは os.File 型の値であるので、下記のファイル書き込み処理が使える。

package main

import (
	"fmt"
	"os"
)

func main() {
	// 文字列を書き込み
	size, err := os.Stdout.WriteString("Hello World\n")
	fmt.Println(size, err) // == 12 <nil>

	// フォーマット指定で書き込み
	fmt.Fprintf(os.Stdout, "The number is %d\n", 42)
}

play_circleRun open_in_newRun In The Playground

参考ドキュメント: os , fmt

エラー出力に書き込む #

エラー出力は os パッケージの変数 os.Stderr である。

これは os.File 型の値であるので、下記のファイル書き込み処理が使える。

package main

import (
	"fmt"
	"os"
)

func main() {
	// 文字列を書き込み
	size, err := os.Stderr.WriteString("Hello World\n")
	fmt.Println(size, err) // == 12 <nil>

	// フォーマット指定で書き込み
	fmt.Fprintf(os.Stderr, "The number is %d\n", 42)
}

play_circleRun open_in_newRun In The Playground

参考ドキュメント: os , fmt

コマンドライン引数を取得する #

コマンドライン引数は os パッケージの変数 os.Args に入る。

これは単なる []string であり、0 番目の要素が実行ファイル名、それ以降が引数となる。

package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Printf("%#v", os.Args)
}

/* 実行コマンドと実行結果
$ go run main.go 1 2 3
[]string{"/var/folders/wm/qmc3zr9n11x5yp1rgqx7rsp40000gp/T/go-build1344239269/b001/exe/main", "1", "2", "3"}

$ go run main.go -a 1 -b 2 -c 3
[]string{"/var/folders/wm/qmc3zr9n11x5yp1rgqx7rsp40000gp/T/go-build1270504581/b001/exe/main", "-a", "1", "-b", "2", "-c", "3"}
 */

play_circleRun open_in_newRun In The Playground

参考ドキュメント: os

コマンドラインオプションを取得する #

flag パッケージを使ってコマンドラインオプションを取得する。

flag を使うとコマンドライン引数とコマンドラインオプションを区別して取得できる。

オプションは変数として flag.Bool などの専用関数で定義しておき、実行時にパースする。

オプション同士の順番の前後は定義順とは関係ないが、引数はオプションの後に指定する必要がある。

package main

import (
	"flag"
	"fmt"
)

// オプションの定義
// オプションを受ける変数とオプション名、デフォルト値、説明を定義する
var b = flag.Bool("b", false, "bool option usage")
var s = flag.String("str", "default value", "string option usage")
var i = flag.Int("i", 10, "int option test")
var f = flag.Float64("f", .1, "float option usage")

func main() {
	// オプションをパースする
	flag.Parse()

	// オプションの値を表示
	fmt.Printf("b: %#v\n", *b)
	fmt.Printf("s: %#v\n", *s)
	fmt.Printf("i: %#v\n", *i)
	fmt.Printf("f: %#v\n", *f)

	// 引数を表示
	fmt.Printf("args: %#v\n", flag.Args())
}

/* 実行コマンドと実行結果

$ go run main.go
b: false
s: "default value"
i: 10
f: 0.1
args: []string{}

$ go run main.go -str hello -b -i 100 -f 2.2 foo bar baz
b: true
s: "hello"
i: 100
f: 2.2
args: []string{"foo", "bar", "baz"}

*/

play_circleRun open_in_newRun In The Playground

参考ドキュメント: flag

データを出力するものを io.Reader として抽象化する #

Go 言語ではデータを出力するものはio.Reader インターフェイスとして抽象化される。

io.Reader を実装するものは例えば、ファイルハンドルや HTTP のレスポンスやデータ変換処理結果など様々である。

これら様々なものが io.Reader として抽象化されることで、データ読み込み処理が再利用しやすく便利になる。

io.ReaderRead メソッドのみを要求する単純なインターフェイスである。

type Reader interface {
	Read(p []byte) (n int, err error)
}

以下の例では io.Reader の代表的な実装である os.Fileio.Reader として扱いデータを読み込んでみる。

package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
)

func main() {
	// ファイルの作成
	err := os.WriteFile("file.txt", []byte("Hello World"), 0666)
	if err != nil {
		panic(err)
	}

	// os.File の取得
	f, err := os.Open("file.txt")
	if err != nil {
		panic(err)
	}

	// ファイルを io.Reader としてデータの読み込み
	data := read(f)
	fmt.Println(string(data)) // == Hello World
}

func read(r io.Reader) []byte {
	buf := bytes.Buffer{}
	b := make([]byte, 10)
	for {
		n, err := r.Read(b)
		if err == io.EOF {
			break
		}
		buf.Write(b[:n])
	}
	return buf.Bytes()
}

play_circleRun open_in_newRun In The Playground

参考ドキュメント: bytes , io , os

io.Reader からデータを読み込む #

io.Reader から行ごとにデータを読み込む方法と、一定のサイズごとに読み込む方法は下記を参照のこと。

ファイルハンドルである os.Fileio.Reader を実装するので、ファイルからデータを読む方法はそのまま io.Reader でも利用できる場合が多い。

io.Reader から全てのデータを一度の読み込む方法はこちら。

package main

import (
	"fmt"
	"io"
	"strings"
)

func main() {
	// 文字列から io.Reader を実装する strings.Reader を作成する
	r := strings.NewReader("hello world")
	// データを読み込む
	data, err := io.ReadAll(r)
	fmt.Println(string(data), err) // == hello world <nil>
}

play_circleRun open_in_newRun In The Playground

参考ドキュメント: bytes , io , strings

io.Reader のデータを加工して返すフィルタリング関数を作成する #

io.Reader のデータに一定間隔ごとに改行を挿入する関数を作成する

package main

import (
	"bytes"
	"fmt"
	"io"
	"strings"
)

func main() {
	r := strings.NewReader("hello world")
	data, err := io.ReadAll(newLineInjector(r, 3))
	fmt.Println(string(data), err)
	/* 出力
	hel
	lo
	wor
	ld
	 <nil>
	*/

	r.Reset("hello world")
	data, err = io.ReadAll(newLineInjector(r, 5))
	fmt.Println(string(data), err)
	/* 出力
	hello
	 worl
	d
	 <nil>

	*/
}

func newLineInjector(r io.Reader, size int) io.Reader {
	buf := new(bytes.Buffer)
	b := make([]byte, size)
	for {
		n, err := r.Read(b)
		if err == io.EOF {
			break
		}
		if err != nil {
			panic(err)
		}
		buf.Write(b[:n])
		buf.Write([]byte{'\n'})
	}
	return buf
}

play_circleRun open_in_newRun In The Playground

参考ドキュメント: bytes , io , strings

データを入力するものを io.Writer として抽象化する #

Go 言語ではデータを入力するものはio.Writer インターフェイスとして抽象化される。

io.Writer を実装するものは例えば、ファイルハンドルや標準出力やデータ変換処理への入力など様々である。

これら様々なものが io.Writer として抽象化されることで、データ書き込み処理が再利用しやすく便利になる。

io.WriteWrite メソッドのみを要求する単純なインターフェイスである。

type Writer interface {
    Write(p []byte) (n int, err error)
}

以下の例では io.Writer の代表的な実装である os.Fileio.Write として扱いデータを書き込んでみる。

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
	// ファイルの作成
	f, err := os.OpenFile("file.txt", os.O_RDWR|os.O_CREATE, 0755)
	if err != nil {
		panic(err)
	}

	// ファイルへ書き込む
	if err := write(f); err != nil {
		panic(err)
	}

	// 書き込みの確認
	data, err := os.ReadFile("file.txt")
	fmt.Println(string(data), err) // == Hello World <nil>
}

func write(w io.Writer) error {
	_, err := w.Write([]byte("Hello World"))
	return err
}

play_circleRun open_in_newRun In The Playground

参考ドキュメント: fmt , io , os