Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 15313] ファイルの概要

このコミットは、Go言語の標準ライブラリである path/filepath パッケージに、SplitList 関数と Rel 関数の使用例を追加するものです。具体的には、Unix系システムでの動作を示す src/pkg/path/filepath/example_unix_test.go という新しいテストファイルが追加されました。このファイルは、SplitList がパスリスト文字列をどのように分割するか、そして Rel が2つのパス間の相対パスをどのように計算するかを、具体的なコード例と期待される出力で示しています。

コミット

  • コミットハッシュ: 04567299771d99206101e3273b1851518cad491a
  • 作者: Kamil Kisiel kamil@kamilkisiel.net
  • コミット日時: 2013年2月19日 火曜日 10:41:35 -0800

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/04567299771d99206101e3273b1851518cad491a

元コミット内容

path/filepath: add examples for SplitList and Rel.

R=golang-dev, bradfitz, minux.ma
CC=golang-dev
https://golang.org/cl/7291043

変更の背景

Go言語の標準ライブラリは、その堅牢性と使いやすさで知られていますが、各関数の具体的な使用方法や挙動を理解するためには、公式ドキュメントだけでなく、具体的なコード例が非常に役立ちます。特に path/filepath パッケージのようなファイルパス操作に関連する関数は、オペレーティングシステムごとのパス区切り文字の違いや、相対パスの計算におけるエッジケースなど、直感的に理解しにくい側面を持つことがあります。

このコミットは、SplitListRel という2つの重要な関数について、その典型的な使用例と、特定の入力に対する期待される出力を示すことで、開発者がこれらの関数をより正確に、そして効率的に利用できるようにすることを目的としています。これにより、ドキュメントの補完と、ライブラリの利用促進が図られています。

前提知識の解説

Go言語の path/filepath パッケージ

path/filepath パッケージは、Go言語でファイルパスを操作するためのユーティリティ関数を提供します。このパッケージは、オペレーティングシステム(OS)に依存しないパス操作を可能にする path パッケージとは異なり、実行中のOSのパス区切り文字(Unix系では /、Windowsでは \)やパスリスト区切り文字(Unix系では :、Windowsでは ;)を考慮して動作します。これにより、クロスプラットフォームなアプリケーション開発において、ファイルパスの正規化、結合、分割、相対パスの計算などを安全に行うことができます。

filepath.SplitList 関数

filepath.SplitList 関数は、OSのパスリスト区切り文字(PathListSeparator)で区切られた文字列を、個々のパスのリストに分割します。例えば、Unix系システムでは環境変数 PATH のような形式の文字列(例: /usr/local/bin:/usr/bin:/bin)を、個々のディレクトリパスの配列に変換する際に使用されます。

filepath.Rel 関数

filepath.Rel 関数は、base パスから target パスへの相対パスを計算します。例えば、/home/user/documentsbase とし、/home/user/documents/report.txttarget とした場合、Relreport.txt を返します。この関数は、ファイルシステム上の位置関係を簡潔に表現するために非常に有用です。ただし、basetarget が異なるドライブや異なるファイルシステム上にある場合など、相対パスを計算できない場合にはエラーを返します。

技術的詳細

filepath.SplitList の挙動

filepath.SplitList は、内部的にOS固有の PathListSeparator を使用して文字列を分割します。Unix系システムでは PathListSeparator はコロン(:)であり、Windowsではセミコロン(;)です。この関数は、空のパス要素も適切に処理し、結果のリストに含めます。例えば、a::b のような入力は ["a", "", "b"] と分割されます。

filepath.Rel の挙動とエッジケース

filepath.Rel(base, target) は、以下のロジックに基づいて相対パスを計算します。

  1. 共通のプレフィックスの特定: basetarget の両方から共通のプレフィックス(最も長い共通の親ディレクトリ)を特定します。
  2. base から共通プレフィックスへの移動: base から共通プレフィックスに戻るために必要な .. (親ディレクトリ) の数を計算します。
  3. 共通プレフィックスから target への移動: 共通プレフィックスから target に到達するために必要なパスの残りの部分を結合します。

エッジケース:

  • basetarget が同じ: Rel("/a/b", "/a/b"). を返します。
  • targetbase の子孫: Rel("/a", "/a/b/c")b/c を返します。
  • basetarget の子孫: Rel("/a/b/c", "/a")../../ を返します。
  • basetarget が共通の親を持つが、一方が他方の祖先ではない: Rel("/a/b", "/a/c")../c を返します。
  • 相対パスを計算できない場合:
    • 異なるドライブレター(Windowsの場合): Rel("C:/a", "D:/b") はエラーを返します。
    • base が絶対パスで target が相対パス、またはその逆の場合: Rel("/a", "b/c") のように、basetarget の種類(絶対パスか相対パスか)が異なる場合、Rel はエラーを返すことがあります。今回の例では、base が絶対パス /a で、target が相対パス ./b/c の場合にエラー Rel: can't make b/c relative to /a が発生しています。これは、Rel が同じルート(または同じ相対的な基準)を持つパス間でしか相対パスを計算できないためです。

example_unix_test.go ファイルの役割

Go言語では、_test.go で終わるファイルはテストファイルとして扱われます。Example 関数は、Goのドキュメンテーションツール godoc によって自動的に抽出され、パッケージのドキュメントにコード例として表示されます。これにより、ユーザーは go doc コマンドや pkg.go.dev などのオンラインドキュメントを通じて、関数の使用例を直接確認できます。

また、// Output: コメントは、go test コマンドでテストを実行する際に、例の出力がこのコメントと一致するかどうかを検証するために使用されます。これにより、コード例が常に最新の挙動を反映していることが保証されます。

// +build !windows,!plan9 というビルドタグは、このファイルがWindowsおよびPlan 9以外のOSでのみコンパイルされることを示しています。これは、path/filepath パッケージの挙動がOSによって異なるため、Unix系システムに特化した例であることを明示するためです。

コアとなるコードの変更箇所

src/pkg/path/filepath/example_unix_test.go という新しいファイルが追加されました。

--- /dev/null
+++ b/src/pkg/path/filepath/example_unix_test.go
@@ -0,0 +1,39 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !windows,!plan9
+
+package filepath_test
+
+import (
+	"fmt"
+	"path/filepath"
+)
+
+func ExampleSplitList() {
+	fmt.Println("On Unix:", filepath.SplitList("/a/b/c:/usr/bin"))
+	// Output:
+	// On Unix: [/a/b/c /usr/bin]
+}
+
+func ExampleRel() {
+	paths := []string{
+		"/a/b/c",
+		"/b/c",
+		"./b/c",
+	}
+	base := "/a"
+
+	fmt.Println("On Unix:")
+	for _, p := range paths {
+		rel, err := filepath.Rel(base, p)
+		fmt.Printf("%q: %q %v\n", p, rel, err)
+	}
+
+	// Output:
+	// On Unix:
+	// "/a/b/c": "b/c" <nil>
+	// "/b/c": "../b/c" <nil>
+	// "./b/c": "" Rel: can't make b/c relative to /a
+}

コアとなるコードの解説

ExampleSplitList 関数

この関数は filepath.SplitList の基本的な使用法を示しています。

func ExampleSplitList() {
	fmt.Println("On Unix:", filepath.SplitList("/a/b/c:/usr/bin"))
	// Output:
	// On Unix: [/a/b/c /usr/bin]
}
  • 入力文字列 "/a/b/c:/usr/bin" は、Unix系システムにおける典型的なパスリスト形式です。
  • filepath.SplitList は、この文字列をコロン(:)で分割し、[]string{"/a/b/c", "/usr/bin"} という文字列スライスを返します。
  • fmt.Println で出力される結果は、// Output: コメントに記載されている通り On Unix: [/a/b/c /usr/bin] となります。

ExampleRel 関数

この関数は filepath.Rel の様々なシナリオでの使用法と、エラーハンドリングを示しています。

func ExampleRel() {
	paths := []string{
		"/a/b/c",
		"/b/c",
		"./b/c",
	}
	base := "/a"

	fmt.Println("On Unix:")
	for _, p := range paths {
		rel, err := filepath.Rel(base, p)
		fmt.Printf("%q: %q %v\n", p, rel, err)
	}

	// Output:
	// On Unix:
	// "/a/b/c": "b/c" <nil>
	// "/b/c": "../b/c" <nil>
	// "./b/c": "" Rel: can't make b/c relative to /a
}
  • base パスは "/a" に設定されています。

  • paths スライスには、異なる種類のターゲットパスが含まれています。

    1. "/a/b/c": base の子孫パスです。
      • filepath.Rel("/a", "/a/b/c")b/c を返します。エラーは発生しません(<nil>)。
    2. "/b/c": base とは異なるルートを持つ絶対パスです。
      • filepath.Rel("/a", "/b/c")../b/c を返します。これは、/a から親ディレクトリに戻り(..)、そこから /b/c へ進むことを意味します。エラーは発生しません(<nil>)。
    3. "./b/c": 相対パスです。
      • filepath.Rel("/a", "./b/c") はエラーを返します。出力は "" Rel: can't make b/c relative to /a となります。これは、Rel 関数が絶対パスと相対パスの間で相対パスを計算できないためです。base が絶対パスであるのに対し、target が相対パスであるため、Rel は有効な相対パスを生成できません。

この例は、Rel 関数がどのように動作するか、特に異なるパスタイプ(絶対パスと相対パス)が混在する場合のエラー挙動を明確に示しており、開発者がこれらの関数を安全に利用するための重要な情報を提供しています。

関連リンク

参考にした情報源リンク