ファイルの作成・読出し・書込みなどの操作を「ファイル入出力」と言います。
Goのファイル入出力機能は、 "[https://pkg.go.dev/os os]" パッケージで実装されています。
"os" パッケージで定義された関数  [https://pkg.go.dev/os#Open os.Open]  関数でファイルをオープンできます。
 os.Open  関数を使用する際、この関数は戻り値を2つ返します（多値返却）。
 os.Open  関数の第1戻値は正常動作時の戻値(*os.File)、第2戻値はエラー時に nil 以外を返します。
[https://play.golang.org/p/BBKtL-8hllh ファイルのオープンと読出し（バッファリングなし）]:
package main
import (
	"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:
			os.Stdout.Write(b[:c])
		default:
			panic("???")
		}
	}
}
解説 :
	f, err := os.Open("/etc/hosts")
os.Open 関数は、ディフォルトでは読出しモードでファイルを開きます。
遅延実行:
	defer func() {
		err := f.Close()
		if err != nil {
			log.Fatal(err)
		}
	}()
defer は遅延実行で、関数スコープを抜けるときに実行する処理を登録します。
この場合は main関数を抜けるときに f.Close() が必ず実行され、エラーがあればlogします。
C++のディストラクターと似ていますが、特定のオブジェクトには紐付いてはおらず複数の遅延実行の実行順を制御できるところが違います。
C言語の at_exit()関数とも似ていますが、at_exit()はプログラムの終了のタイミングに実行されるのに対して、defer は関数スコープを抜けたときに実行されます。
一旦 defer で登録処理を取り消す方法はありません。登録された処理側で条件を判定する必要があります。
Goでは goroutine もそうですがgoroutine には、スレッドにありがちな kill や join はなく、チェンネルなどの通信を使って実現します。このため go 文には値がありません（スレッドならスレッドIDを返すところ）。、呼び出し先の生殺与奪の権を呼び出し元に握らせるのではなく、予め決めた手順で通信を行い協調して状態の遷移を管理するスタイルです。
		c, err := f.Read(b)
(*os.File).Readは引数のバッファサイズまでファイルからデータを読みだします。
			os.Stdout.Write(b[:c])
と文字列に変換せずそのまま []byte で書き出します。
		log.Fatal(err)
をコールすると
2009/11/10 23:00:00 open /etc/hosts: no such file or directory
exit status 1
の様に診断メセージを書き出し、exit(1)を呼び出します（呼び出しもとには帰りません。C言語風にいうと _Noreturn 関数修飾子が着いた状態です）。
[https://play.golang.org/p/rQpYVSoS7sj ファイルのオープンと読出し（バッファリングあり）]:
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)
	}
}
解説 :
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
os.Open() f.Close() はバッファリングしない場合と同じです。
目新しいのは [https://pkg.go.dev/bufio#NewScanner bufio.NewScanner()] で、戻値は *bufio.Scanner型 で scanner に代入しました。
scanner.Scan() は変数を渡していませんが、1行分読み込んで次の scanner.Text() で返します。
scanner.Scan() はファイルの終わりまで来ると nil を返します（ので for ループを抜けます）。
エラーが有った場合も scanner.Scan() は nil を返すので scanner.Err() を使ってエラーが出ていたか調べています。
{{コラム|バッファリング|
バッファリングは、複数の機器やプログラムの間でデータを転送する場合に、予め用意した記憶領域（バッファー）に送受信データを一時的に保存する手法で
ことを目的としています。
バッファリングすることで、
などが図れます。
バッファーにある未処理のデータを強制的に次の処理に送り出すことを、フラッシュ（flash）と呼びます。
"os" パッケージで定義された関数  [https://pkg.go.dev/os#Create os.Create]  関数でファイルを作成できます。
 os.Create  関数を使用する際、この関数は戻り値を2つ返します（多値返却）。
 os.Create  関数の第1戻値は正常動作時の戻値(*os.File)、第2戻値はエラー時に nil 以外を返します。
[https://play.golang.org/p/hHJeiwjO3G2 ファイルの作成と書込み]:
package main
import (
	"log"
	"os"
)
func main() {
	f, err := os.Create("file-create-test.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		err := f.Close()
		if err != nil {
			log.Fatal(err)
		}
	}()
	if _, err := f.Write([]byte("TEST TEXT\n")); err != nil {
		log.Fatal(err)
	}
}
解説 :
	if _, err := f.Write([]byte("TEST TEXT\n")); err != nil {
fp.Writeは[]byte（byte型のスライス）を引数にするので、明示的に型変換しています。
遅延実行 :
	defer func() {
		err := f.Close()
		if err != nil {
			log.Fatal(err)
		}
	}()
defer は遅延実行で、関数スコープを抜けるときに実行する処理を登録します。