「Go/ファイル入出力」の版間の差分

削除された内容 追加された内容
Ef3 (トーク | 投稿記録)
{{Nav}}→‎ファイルのオープンと読出し: →‎ファイルの作成と書込み: defer の f.Close() を匿名関数としエラーチェックとログ出力を追加。
タグ: 2017年版ソースエディター
Ef3 (トーク | 投稿記録)
タグ: 2017年版ソースエディター
9 行
 
== ファイルのオープンと読出し ==
 
;[https://play.golang.org/p/BBKtL-8hllh ファイルのオープンと読出し]:<syntaxhighlight lang=go highlight="11,15-20,24,33" line>
=== バッファリングなし ===
;[https://play.golang.org/p/BBKtL-8hllh ファイルのオープンと読出し(バッファリングなし)]:<syntaxhighlight lang=go highlight="1110,1514-2019,2423,3332" line>
package main
 
import (
"fmt"
"io"
"log"
42 ⟶ 43行目:
log.Fatal(err)
case err == nil:
fmtos.Print(stringStdout.Write(b[:c]))
default:
panic("???")
49 ⟶ 50行目:
}
</syntaxhighlight>
; 解説 :<syntaxhighlight lang=go start=1110 line>
f, err := os.Open("/etc/hosts")
</syntaxhighlight>
: os.Open 関数は、ディフォルトでは読出しモードでファイルを開きます。
; 遅延実行:<syntaxhighlight lang=go start="1514-2019" line>
defer func() {
err := f.Close()
67 ⟶ 68行目:
: 一旦 defer で登録処理を取り消す方法はありません。登録された処理側で条件を判定する必要があります。
: Goでは goroutine もそうですが<ref>goroutine には、スレッドにありがちな kill や join はなく、チェンネルなどの通信を使って実現します。このため go 文には値がありません(スレッドならスレッドIDを返すところ)。</ref>、呼び出し先の生殺与奪の権を呼び出し元に握らせるのではなく、予め決めた手順で通信を行い協調して状態の遷移を管理するスタイルです。
:<syntaxhighlight lang=go start=2423 line>
c, err := f.Read(b)
</syntaxhighlight>
: (*os.File).Readは引数のバッファサイズまでファイルからデーターを読みだします。
: このとき読まれるのはバイト配列なので
:<syntaxhighlight lang=go start=3332 line>
fmt.Print(string(b[:c]))
</syntaxhighlight>
: のように string に変換し表示します。
:<syntaxhighlight lang=go start=33 line>
os.Stdout.Write(b[:c])
</syntaxhighlight>
: と文字列に変換せずそのまま []byte で書き出します。
: とすれば []byte のまま書出せます。<!-- 書いていて気がついたのですが、os.Stdout.Writeに書き換えると fmt をインポートする必要がなくなり、行番号が滑るので見合わせました。 -->
 
=== エラーハンドリング ===
91 ⟶ 88行目:
</syntaxhighlight>
の様に診断メセージを書き出し、exit(1)を呼び出します(呼び出しもとには帰りません。C言語風にいうと _Noreturn 関数修飾子が着いた状態です)。
 
=== バッファリングあり ===
;[https://play.golang.org/p/rQpYVSoS7sj ファイルのオープンと読出し(バッファリングあり)]:<syntaxhighlight lang=go highlight="22-28" line>
package main
 
import (
"bufio"
"fmt"
"log"
"os"
)
 
func main() {
f, err := os.Open("/etc/hosts")
if err != nil {
log.Fatal(err)
}
defer func() {
err := f.Close()
if err != nil {
log.Fatal(err)
}
}()
 
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}
</syntaxhighlight>
; 解説 :<syntaxhighlight lang=go start=3322 line>
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
</syntaxhighlight>
: os.Open() f.Close() はバッファリングしない場合と同じです。
: 目新しいのは [https://pkg.go.dev/bufio#NewScanner bufio.NewScanner()] で、戻値は *bufio.Scanner型 で scanner に代入しました。
: scanner.Scan() は変数を渡していませんが、1行分読み込んで次の scanner.Text() で返します。
: scanner.Text() 名前からも判るようにバイト列ではなくテキストを返します(行末の改行は取り除かれます)
: scanner.Scan() はファイルの終わりまで来ると nil わ返します(ので for ループを抜けます)。
: エラーが有った場合も scanner.Scan() は nil を返すので scanner.Err() を使ってエラーが出ていたか調べています。
 
{{コラム|バッファリング|
バッファリングは、
# 複数の機器やプログラムの間でデーターを転送する場合で、処理速度や転送速度の差を補う
# 通信の減速や中断に備え、予め用意した記憶領域に送受信データを一時的に保存する
などの目的に設けられる記憶領域です。
 
バッファリングすることで、
# 動作速度の向上
# 入出力要求の回数の減少
# ジッターの防止あるいは低減
などが図れます。
 
バッファーにある未処理のデーターを強制的に次の処理に送り出すことを、フラッシュ(flash)と呼びます。
}}
 
== ファイルの作成と書込み ==