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

削除された内容 追加された内容
Ef3 (トーク | 投稿記録)
defer で f.Close()
タグ: 2017年版ソースエディター
Ef3 (トーク | 投稿記録)
{{Nav}}→‎ファイルのオープンと読出し: →‎ファイルの作成と書込み: defer の f.Close() を匿名関数としエラーチェックとログ出力を追加。
タグ: 2017年版ソースエディター
1 行
{{Nav}}
外部ファイルを作成したり、外部ファイルに書き込んだりすることを「ファイル入出力」と言います。
= ファイル入出力 =
 
ファイルの作成・読出し・書込みなどの操作を「ファイル入出力」と言います。
Goのファイル入出力機能は、 "[https://pkg.go.dev/os os]" パッケージで実装されています。
"os" パッケージで定義された関数 <code> [https://pkg.go.dev/os#CreateOpen os.CreateOpen] </code> 関数で、外部ファイルを作成オープンできます。
<code> os.Open </code> 関数を使用する際、この関数は戻り値を2つ返します(多値返却)。
<code> os.Open </code> 関数の第1戻値は正常動作時の戻値(*os.File)、第2戻値はエラー時に nil 以外を返します。
 
== ファイルのオープンと読出し ==
;[https://play.golang.org/p/BBKtL-8hllh ファイルのオープンと読出し]:<syntaxhighlight lang=go highlight="11,15-20,24,33" line>
package main
 
import (
"fmt"
"io"
"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)
}
}()
 
b := make([]byte, 4096)
for {
c, err := f.Read(b)
switch {
case c == 0:
return
case err == io.EOF:
return
case err != nil:
log.Fatal(err)
case err == nil:
fmt.Print(string(b[:c]))
default:
panic("???")
}
}
}
</syntaxhighlight>
; 解説 :<syntaxhighlight lang=go start=11 line>
f, err := os.Open("/etc/hosts")
</syntaxhighlight>
: os.Open 関数は、ディフォルトでは読出しモードでファイルを開きます。
; 遅延実行:<syntaxhighlight lang=go start="15-20" line>
defer func() {
err := f.Close()
if err != nil {
log.Fatal(err)
}
}()
</syntaxhighlight>
: '''defer''' は遅延実行で、関数スコープを抜けるときに実行する処理を登録します。
: この場合は main関数を抜けるときに <code>f.Close()</code> が必ず実行され、エラーがあればlogします。
: [[C++]]のディストラクターと似ていますが、特定のオブジェクトには紐付いてはおらず複数の遅延実行の実行順を制御できるところが違います。
: [[C言語]]の at_exit()関数とも似ていますが、at_exit()はプログラムの終了のタイミングに実行されるのに対して、defer は関数スコープを抜けたときに実行されます。
: 一旦 defer で登録処理を取り消す方法はありません。登録された処理側で条件を判定する必要があります。
: Goでは goroutine もそうですが<ref>goroutine には、スレッドにありがちな kill や join はなく、チェンネルなどの通信を使って実現します。このため go 文には値がありません(スレッドならスレッドIDを返すところ)。</ref>、呼び出し先の生殺与奪の権を呼び出し元に握らせるのではなく、予め決めた手順で通信を行い協調して状態の遷移を管理するスタイルです。
:<syntaxhighlight lang=go start=24 line>
c, err := f.Read(b)
</syntaxhighlight>
: (*os.File).Readは引数のバッファサイズまでファイルからデーターを読みだします。
: このとき読まれるのはバイト配列なので
:<syntaxhighlight lang=go start=33 line>
fmt.Print(string(b[:c]))
</syntaxhighlight>
: のように string に変換し表示します。
:<syntaxhighlight lang=go start=33 line>
os.Stdout.Write(b[:c])
</syntaxhighlight>
: とすれば []byte のまま書出せます。<!-- 書いていて気がついたのですが、os.Stdout.Writeに書き換えると fmt をインポートする必要がなくなり、行番号が滑るので見合わせました。 -->
 
=== エラーハンドリング ===
: <syntaxhighlight lang=go>
log.Fatal(err)
</syntaxhighlight>
をコールすると
:<syntaxhighlight lang=text>
2009/11/10 23:00:00 open /etc/hosts: no such file or directory
exit status 1
</syntaxhighlight>
の様に診断メセージを書き出し、exit(1)を呼び出します(呼び出しもとには帰りません。C言語風にいうと _Noreturn 関数修飾子が着いた状態です)。
 
== ファイルの作成と書込み ==
"os" パッケージで定義された関数 <code> [https://pkg.go.dev/os#Create os.Create] </code> 関数でファイルを作成できます。
<code> os.Create </code> 関数を使用する際、この関数は戻り値を2つ返します(多値返却)。
<code> os.Create </code> 関数の第1引数戻値正常動作時の戻(*os.File)、第2引数戻値はエラー時に nil 以外を返します。
 
;[https://play.golang.org/p/DzXKtvcf3ZRhHJeiwjO3G2 コード例ファイルの作成と書込み]:<syntaxhighlight lang=go highlight="13-18,20" line>
package main
 
18 ⟶ 110行目:
log.Fatal(err)
}
defer f.Closefunc() {
err := f.Close()
if err != nil {
log.Fatal(err)
}
}()
 
if _, err := f.Write([]byte("TEST TEXT\n")); err != nil {
25 ⟶ 122行目:
}
</syntaxhighlight>
; 解説 :<syntaxhighlight lang=go start=1520 line>
if _, err := f.Write([]byte("TEST TEXT\n")); err != nil {
</syntaxhighlight>
: fp.Write は[]byte(byte型のスライス)を引数にするので、明示的に型変換しています。
; 遅延実行 :<syntaxhighlight lang=go start=13 line>
defer f.Closefunc() {
err := f.Close()
</syntaxhighlight>
if err != nil {
: '''defer''' 遅延実行で、スコープを抜けるときに実行する処理を登録します。
log.Fatal(err)
: この場合は main関数を抜けるときに <code>f.Close()</code> が必ず実行されます。
}
: [[C++]]のディストラクターと似ていますが、オブジェクトに紐付いてはおらず複数の遅延実行の実行順を制御できるところが違います。
}()
: [[C言語]]の at_exit()関数とも似ていますが、at_exit()はプログラムの終了のタイミングに実行されるのに対して、defer は関数やブロックのスコープを抜けたときに実行されます。
: 一旦 defer で登録処理を取り消す方法はありません。登録された処理側で条件判定する必要があります。
: Goでは goroutine もそうですが<ref>goroutine には、スレッドにありがちな kill や join はなく、チェンネルなどの通信を使って実現します。このため go 文には値がありません(スレッドならスレッドIDを返すところ)。</ref>、呼び出し先の生殺与奪の権を呼び出し元に握らせるのではなく、ある種の通信を行い協調して状態の遷移を管理するスタイルです。
; エラーハンドリング : <syntaxhighlight lang=go>
log.Fatal(err)
</syntaxhighlight>
をコールすると
:<syntaxhighlight lang=text>
2021/09/19 12:44:27 open file-create-test.txt: permission denied
exit status 1
</syntaxhighlight>
: '''defer''' は遅延実行で、関数スコープを抜けるときに実行する処理を登録します。
の様に診断メセージを書き出します。
 
== 脚註 ==