[インデックス 15116] ファイルの概要
このコミットは、Go言語の標準ライブラリ archive/tar
パッケージに、tarアーカイブの読み書きに関する具体的なコード例を追加するものです。これにより、ユーザーがこのパッケージをより簡単に理解し、利用できるようになります。既存のコメント形式の例は削除され、example_test.go
という新しいファイルに、実行可能なテスト形式の例が追加されました。
コミット
commit 97916f11548110b282c460aa9f939bac139ca99c
Author: Robin Eklind <r.eklind.87@gmail.com>
Date: Mon Feb 4 12:37:18 2013 +1100
archive/tar: Add reader and writer code example.
Remove the previous comment examples.
R=golang-dev, minux.ma, adg
CC=golang-dev
https://golang.org/cl/7220048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/97916f11548110b282c460aa9f939bac139ca99c
元コミット内容
このコミットの目的は、archive/tar
パッケージにtarアーカイブの読み書きのコード例を追加することです。具体的には、以前のコメント形式の例を削除し、代わりに example_test.go
という新しいファイルに、実行可能なテスト形式の例を追加しています。
変更の背景
Go言語の標準ライブラリでは、各パッケージの利用方法を示すために、Example
関数を用いたコード例を提供することが推奨されています。これらの Example
関数は、go test
コマンドを実行する際に自動的にテストされ、その出力が期待される出力と一致するかどうかが検証されます。これにより、コード例が常に最新かつ正確であることが保証されます。
このコミット以前は、archive/tar
パッケージの reader.go
と writer.go
ファイル内に、コメント形式で簡単な使用例が記述されていました。しかし、これらのコメント例は自動テストの対象外であり、コードの変更に伴って古くなったり、誤りを含んだりする可能性がありました。
このコミットの背景には、以下の目的があります。
- コード例の信頼性向上:
Example
関数としてコード例を記述することで、go test
による自動検証が可能になり、例が常に正しく動作することを保証します。 - ドキュメントの改善:
go doc
コマンドやGoの公式ドキュメントサイト(pkg.go.devなど)で、Example
関数が自動的に抽出され、パッケージのドキュメントとして表示されます。これにより、ユーザーはより簡単にパッケージの使用方法を学ぶことができます。 - 開発者の利便性向上: 開発者がパッケージの機能を理解し、利用する際の障壁を低減します。
前提知識の解説
Go言語の Example
関数
Go言語では、パッケージの使用例を示すために Example
関数という特別な関数を定義できます。これらの関数は、_test.go
ファイル内に記述され、Example
というプレフィックスを持つ必要があります。
例:
func ExamplePackageFunction() {
// ... コード例 ...
// Output:
// 期待される出力
}
Example
関数の特徴は以下の通りです。
- 自動テスト:
go test
コマンドを実行すると、Example
関数内のコードが実行され、その標準出力が// Output:
コメントに記述された期待される出力と一致するかどうかが検証されます。これにより、コード例が常に正しく動作することが保証されます。 - ドキュメント生成:
go doc
コマンドやGoの公式ドキュメントサイト(pkg.go.devなど)で、Example
関数が自動的に抽出され、パッケージのドキュメントとして表示されます。これにより、ユーザーはパッケージの利用方法を簡単に参照できます。 - 可読性: コード例が独立した関数として記述されるため、可読性が向上します。
archive/tar
パッケージ
archive/tar
パッケージは、TAR(Tape Archive)形式のファイルを読み書きするためのGo言語の標準ライブラリです。TARファイルは、複数のファイルを一つのアーカイブにまとめるための一般的な形式であり、主にファイルのバックアップや配布に使用されます。
このパッケージの主要なコンポーネントは以下の通りです。
tar.Reader
: TARアーカイブからファイルを読み取るための構造体です。Next()
メソッドを使用してアーカイブ内の次のファイルに移動し、io.Reader
インターフェースを実装しているため、ファイルの内容を読み取ることができます。tar.Writer
: TARアーカイブにファイルを書き込むための構造体です。WriteHeader()
メソッドを使用して新しいファイルのヘッダーを書き込み、Write()
メソッドを使用してファイルの内容を書き込みます。tar.Header
: TARアーカイブ内の各ファイルのエントリ(メタデータ)を表す構造体です。ファイル名、サイズ、パーミッション、タイムスタンプなどの情報が含まれます。
TARファイルの構造は、基本的に各ファイルのヘッダー(メタデータ)とそれに続くファイルデータが連続して配置される形式です。tar.Reader
はこの構造を解析し、tar.Writer
はこの構造を生成します。
技術的詳細
このコミットでは、archive/tar
パッケージの Example
関数が example_test.go
ファイルに新しく追加されました。この Example
関数は、tarアーカイブの作成(書き込み)と読み取りの両方のプロセスを包括的に示しています。
Tarアーカイブの作成(書き込み)
bytes.Buffer
の利用:bytes.Buffer
を使用して、メモリ上にtarアーカイブを構築します。これにより、ファイルシステムに直接書き込むことなく、アーカイブの作成と読み取りの例を完結させることができます。tar.NewWriter
:tar.NewWriter(buf)
を呼び出して、bytes.Buffer
に書き込むためのtar.Writer
インスタンスを作成します。- ファイルの追加:
tar.Header
構造体を作成し、ファイル名 (Name
) とファイルサイズ (Size
) を設定します。tw.WriteHeader(hdr)
を呼び出して、ファイルのヘッダーをtarアーカイブに書き込みます。tw.Write([]byte(file.Body))
を呼び出して、ファイルの内容をtarアーカイブに書き込みます。- このプロセスをループで繰り返し、複数のファイルをアーカイブに追加します。
tw.Close()
: すべてのファイルの書き込みが完了したら、tw.Close()
を呼び出してtar.Writer
を閉じます。これにより、必要なフッター情報がアーカイブに書き込まれ、アーカイブが正しく閉じられます。
Tarアーカイブの読み取り
bytes.NewReader
の利用: 作成したbytes.Buffer
の内容を読み取るために、bytes.NewReader(buf.Bytes())
を使用してio.Reader
インターフェースを満たすリーダーを作成します。tar.NewReader
:tar.NewReader(r)
を呼び出して、tarアーカイブを読み取るためのtar.Reader
インスタンスを作成します。- ファイルの読み取り:
- 無限ループ内で
tr.Next()
を呼び出します。このメソッドは、アーカイブ内の次のファイルのエントリ(tar.Header
)を返します。 io.EOF
エラーが返された場合、アーカイブの終わりに達したことを意味するため、ループを終了します。- それ以外のエラーが発生した場合は、エラーハンドリングを行います。
fmt.Printf("Contents of %s:\\n", hdr.Name)
でファイル名を表示します。io.Copy(os.Stdout, tr)
を使用して、現在のファイルの内容を標準出力にコピーします。tr
はio.Reader
インターフェースを実装しているため、ファイルの内容を直接読み取ることができます。
- 無限ループ内で
既存コメントの削除
src/pkg/archive/tar/reader.go
と src/pkg/archive/tar/writer.go
から、以前のコメント形式のコード例が削除されました。これは、新しい Example
関数がより信頼性が高く、ドキュメントとして優れているため、重複する情報が不要になったためです。
コアとなるコードの変更箇所
src/pkg/archive/tar/example_test.go
(新規追加)
--- /dev/null
+++ b/src/pkg/archive/tar/example_test.go
@@ -0,0 +1,75 @@
+package tar_test
+
+import (
+ "archive/tar"
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "os"
+)
+
+func Example() {
+ // Create a buffer to write our archive to.
+ buf := new(bytes.Buffer)
+
+ // Create a new tar archive.
+ tw := tar.NewWriter(buf)
+
+ // Add some files to the archive.
+ var files = []struct {
+ Name, Body string
+ }{
+ {"readme.txt", "This archive contains some text files."},
+ {"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
+ {"todo.txt", "Get animal handling licence."},
+ }
+ for _, file := range files {
+ hdr := &tar.Header{
+ Name: file.Name,
+ Size: int64(len(file.Body)),
+ }
+ if err := tw.WriteHeader(hdr); err != nil {
+ log.Fatalln(err)
+ }
+ if _, err := tw.Write([]byte(file.Body)); err != nil {
+ log.Fatalln(err)
+ }
+ }
+ // Make sure to check the error on Close.
+ if err := tw.Close(); err != nil {
+ log.Fatalln(err)
+ }
+
+ // Open the tar archive for reading.
+ r := bytes.NewReader(buf.Bytes())
+ tr := tar.NewReader(r)
+
+ // Iterate through the files in the archive.
+ for {
+ hdr, err := tr.Next()
+ if err == io.EOF {
+ // end of tar archive
+ break
+ }
+ if err != nil {
+ log.Fatalln(err)
+ }
+ fmt.Printf("Contents of %s:\\n", hdr.Name)
+ if _, err := io.Copy(os.Stdout, tr); err != nil {
+ log.Fatalln(err)
+ }
+ fmt.Println()
+ }
+
+ // Output:
+ // Contents of readme.txt:
+ // This archive contains some text files.
+ // Contents of gopher.txt:
+ // Gopher names:
+ // George
+ // Geoffrey
+ // Gonzo
+ // Contents of todo.txt:
+ // Get animal handling licence.
+}
src/pkg/archive/tar/reader.go
(コメント削除)
--- a/src/pkg/archive/tar/reader.go
+++ b/src/pkg/archive/tar/reader.go
@@ -25,20 +25,6 @@ var (
// A tar archive consists of a sequence of files.
// The Next method advances to the next file in the archive (including the first),
// and then it can be treated as an io.Reader to access the file's data.
-//
-// Example:
-// tr := tar.NewReader(r)
-// for {
-// hdr, err := tr.Next()
-// if err == io.EOF {
-// // end of tar archive
-// break
-// }
-// if err != nil {
-// // handle error
-// }
-// io.Copy(data, tr)
-// }
type Reader struct {
r io.Reader
err error
src/pkg/archive/tar/writer.go
(コメント削除)
--- a/src/pkg/archive/tar/writer.go
+++ b/src/pkg/archive/tar/writer.go
@@ -25,17 +25,6 @@ var (
// A tar archive consists of a sequence of files.
// Call WriteHeader to begin a new file, and then call Write to supply that file's data,
// writing at most hdr.Size bytes in total.
-//
-// Example:
-// tw := tar.NewWriter(w)
-// hdr := new(tar.Header)
-// hdr.Size = length of data in bytes
-// // populate other hdr fields as desired
-// if err := tw.WriteHeader(hdr); err != nil {
-// // handle error
-// }
-// io.Copy(tw, data)
-// tw.Close()
type Writer struct {
w io.Writer
err error
コアとなるコードの解説
追加された Example
関数は、archive/tar
パッケージの基本的な使用方法を簡潔かつ網羅的に示しています。
-
パッケージのインポート:
archive/tar
: tarアーカイブの読み書きに必要な主要パッケージ。bytes
: メモリ上でバイトスライスを扱うためのパッケージ。ここではbytes.Buffer
を使用して、tarアーカイブをメモリ上に構築しています。fmt
: フォーマットされたI/O(出力)を行うためのパッケージ。io
: I/Oプリミティブを提供するパッケージ。io.EOF
やio.Copy
などを使用します。log
: エラーロギングのためのパッケージ。os
: オペレーティングシステム機能へのアクセスを提供するパッケージ。ここではos.Stdout
を使用して標準出力に書き出しています。
-
Example()
関数:- この関数は、
go test
実行時に自動的にテストされ、その出力が// Output:
コメントに記述された期待される出力と一致するかどうかが検証されます。 - アーカイブの作成部分:
buf := new(bytes.Buffer)
: tarアーカイブのデータを一時的に保持するためのバッファを作成します。tw := tar.NewWriter(buf)
:buf
に書き込むためのtar.Writer
を作成します。files
スライスには、アーカイブに追加するファイルの名前と内容が定義されています。- ループ内で各ファイルについて以下の処理を行います。
hdr := &tar.Header{Name: file.Name, Size: int64(len(file.Body))}
:tar.Header
を作成し、ファイル名とサイズを設定します。tw.WriteHeader(hdr)
: ヘッダーを書き込みます。tw.Write([]byte(file.Body))
: ファイルの内容を書き込みます。
tw.Close()
:tar.Writer
を閉じ、アーカイブを完成させます。エラーチェックは重要です。
- アーカイブの読み取り部分:
r := bytes.NewReader(buf.Bytes())
:buf
に書き込まれたアーカイブデータを読み取るためのio.Reader
を作成します。tr := tar.NewReader(r)
:r
から読み取るためのtar.Reader
を作成します。- ループ内で各ファイルについて以下の処理を行います。
hdr, err := tr.Next()
: 次のファイルのエントリ(ヘッダー)を読み込みます。if err == io.EOF { break }
:io.EOF
はアーカイブの終端を示します。if err != nil { log.Fatalln(err) }
: その他のエラーは致命的として処理します。fmt.Printf("Contents of %s:\\n", hdr.Name)
: ファイル名を出力します。io.Copy(os.Stdout, tr)
: ファイルの内容を標準出力にコピーします。tr
は現在のファイルの内容を読み取るio.Reader
として機能します。
// Output:
コメント: このコメントブロックは、Example
関数が実行されたときに期待される標準出力を定義します。go test
はこの出力と実際の出力を比較して、テストの合否を判断します。
- この関数は、
この例は、archive/tar
パッケージの Writer
と Reader
の両方の基本的なワークフローを明確に示しており、Go言語の Example
関数のベストプラクティスに従っています。
関連リンク
- Go言語の
archive/tar
パッケージ公式ドキュメント: https://pkg.go.dev/archive/tar - Go言語の
Example
関数に関する公式ブログ記事(Testing with the Go command): https://go.dev/blog/testing (Example functionsのセクションを参照)
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード
- Go言語のテストに関する一般的な慣習とベストプラクティス