[インデックス 10978] ファイルの概要
このコミットは、Go言語の標準ライブラリ path/filepath
パッケージに Dir
関数を追加するものです。既存の Base
関数(パスの最後の要素、つまりファイル名やディレクトリ名を返す)に対応する形で、パスのディレクトリ部分を返す機能が導入されました。これにより、パス操作の対称性が向上し、より直感的なAPIが提供されます。
コミット
commit dd1a34bdae65c8126fc9b36debd856f7a6e47b86
Author: Rob Pike <r@golang.org>
Date: Thu Dec 22 13:58:58 2011 -0800
path/filepath: Dir
There was Base but not Dir, so fill in the gap.
R=n13m3y3r, r, rsc, gustavo
CC=golang-dev
https://golang.org/cl/5503067
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dd1a34bdae65c8126fc9b36debd856f7a6e47b86
元コミット内容
path/filepath: Dir
There was Base but not Dir, so fill in the gap.
R=n13m3y3r, r, rsc, gustavo
CC=golang-dev
https://golang.org/cl/5503067
変更の背景
Go言語の path/filepath
パッケージには、パスの最後の要素(ファイル名やディレクトリ名)を抽出する Base
関数が既に存在していました。しかし、その対となる、パスのディレクトリ部分を抽出する直接的な関数は提供されていませんでした。このコミットは、この機能的なギャップを埋めるために Dir
関数を導入し、パス操作APIの完全性と利便性を向上させることを目的としています。これにより、開発者はパスからディレクトリ部分を簡単に取得できるようになります。
前提知識の解説
path/filepath
パッケージ
path/filepath
パッケージは、Go言語におけるファイルパスの操作をプラットフォームに依存しない形で行うための機能を提供します。WindowsとUnix系システムではパスの区切り文字や命名規則が異なるため、このパッケージはそれらの違いを吸収し、一貫したパス操作APIを提供します。主な機能には、パスの結合、クリーンアップ、絶対パスかどうかの判定、ファイル名やディレクトリ名の抽出などがあります。
Base
関数
path/filepath
パッケージに存在する Base
関数は、与えられたパスの最後の要素(ベース名)を返します。例えば、/a/b/c
の Base
は c
を返し、/a/b/c.txt
の Base
は c.txt
を返します。また、/a/b/
のように末尾がセパレータで終わるパスの場合も、最後の要素(この場合は空文字列)を返します。
パスの構成要素
ファイルパスは通常、ディレクトリ部分とファイル名(または最後のディレクトリ名)部分に分けられます。
- ディレクトリ部分: ファイルやサブディレクトリが格納されている場所を示すパス。
- ファイル名/ベース名: パスの最後の要素で、ファイルやディレクトリ自体の名前。
例えば、/home/user/documents/report.txt
というパスでは、/home/user/documents
がディレクトリ部分、report.txt
がファイル名(ベース名)です。
技術的詳細
新しく追加された Dir
関数は、与えられたパスからディレクトリ部分を抽出します。その実装は、既存の Split
関数と Clean
関数を組み合わせて利用しています。
-
Split(path)
の利用:Split
関数は、パスをディレクトリ部分とファイル名部分に分割します。Dir
関数は、このSplit
関数が返すディレクトリ部分を最初のステップとして取得します。 -
Clean(dir)
の利用:Split
が返したディレクトリ部分は、..
や.
、重複するセパレータなどを含む「汚れた」状態である可能性があります。Clean
関数は、このようなパスを正規化し、簡潔で標準的な形式に変換します。例えば、a/b/../c
はa/c
に、a//b
はa/b
になります。Dir
関数は、このClean
を適用することで、返されるディレクトリパスが常にクリーンな状態であることを保証します。 -
末尾のセパレータの除去:
Clean
されたパスが末尾にパスセパレータ(例:/
)を持つ場合、Dir
関数はそのセパレータを除去します。ただし、パスがルートディレクトリ(例:/
)である場合は、セパレータは除去されません。これは、ルートディレクトリがそれ自体で有効なディレクトリパスであるためです。 -
エッジケースのハンドリング:
- 空のパス: 入力パスが空文字列の場合、
Dir
は.
(カレントディレクトリ)を返します。これは、空のパスがカレントディレクトリを意味するという一般的な慣習に沿っています。 - セパレータのみのパス: 入力パスが
/
や///
のようにセパレータのみで構成されている場合、Dir
は単一のセパレータ(/
)を返します。これはルートディレクトリを示します。
- 空のパス: 入力パスが空文字列の場合、
これらの処理により、Dir
関数は様々な形式のパスに対して、期待されるディレクトリ部分を正確かつ一貫した形式で返します。
コアとなるコードの変更箇所
src/pkg/path/filepath/path.go
// Dir returns the all but the last element of path, typically the path's directory.
// Trailing path separators are removed before processing.
// If the path is empty, Dir returns ".".
// If the path consists entirely of separators, Dir returns a single separator.
// The returned path does not end in a separator unless it is the root directory.
func Dir(path string) string {
dir, _ := Split(path)
dir = Clean(dir)
last := len(dir) - 1
if last > 0 && os.IsPathSeparator(dir[last]) {
dir = dir[:last]
}
if dir == "" {
dir = "."
}
return dir
}
src/pkg/path/filepath/path_test.go
var dirtests = []PathTest{
{"", "."},
{".", "."},
{"/.", "/"},
{"/", "/"},
{"////", "/"},
{"/foo", "/"},
{"x/", "x"},
{"abc", "."},
{"abc/def", "abc"},
{"a/b/.x", "a/b"},
{"a/b/c.", "a/b"},
{"a/b/c.x", "a/b"},
}
func TestDir(t *testing.T) {
for _, test := range dirtests {
if s := filepath.ToSlash(filepath.Dir(test.path)); s != test.result {
t.Errorf("Dir(%q) = %q, want %q", test.path, s, test.result)
}
}
}
コアとなるコードの解説
Dir
関数
func Dir(path string) string {
dir, _ := Split(path) // 1. パスをディレクトリ部分とファイル名部分に分割し、ディレクトリ部分を取得
dir = Clean(dir) // 2. 取得したディレクトリ部分を正規化(クリーンアップ)
last := len(dir) - 1
if last > 0 && os.IsPathSeparator(dir[last]) { // 3. 末尾のセパレータを除去(ルートディレクトリ以外)
dir = dir[:last]
}
if dir == "" { // 4. パスが空の場合、"." を返す
dir = "."
}
return dir // 5. 最終的なディレクトリパスを返す
}
dir, _ := Split(path)
: 入力されたpath
をSplit
関数に渡し、ディレクトリ部分とファイル名部分に分割します。ここではディレクトリ部分のみが必要なので、ファイル名部分は_
で破棄しています。dir = Clean(dir)
:Split
から得られたdir
をClean
関数で正規化します。これにより、冗長なセパレータや.
、..
などが適切に処理され、パスが簡潔になります。last := len(dir) - 1; if last > 0 && os.IsPathSeparator(dir[last]) { dir = dir[:last] }
: クリーンアップされたdir
の末尾がパスセパレータ(例:/
)である場合、そのセパレータを除去します。ただし、last > 0
の条件により、パスが単一のセパレータ(ルートディレクトリ/
)である場合は除去されません。if dir == "" { dir = "." }
: 上記の処理の結果、dir
が空文字列になった場合(例:abc
のDir
は.
となるべき)、dir
を.
に設定します。return dir
: 最終的に整形されたディレクトリパスを返します。
TestDir
関数
TestDir
関数は、Dir
関数の動作を検証するためのテストケースを定義しています。
dirtests
変数には、入力パスと期待されるDir
関数の結果のペアが複数定義されています。これには、空文字列、カレントディレクトリ、ルートディレクトリ、末尾にセパレータがあるパス、ファイル名を含むパスなど、様々なエッジケースが含まれています。- テストループでは、
dirtests
の各要素に対してfilepath.Dir
を呼び出し、その結果をfilepath.ToSlash
で正規化(Windows環境でのテストを考慮)した上で、期待される結果と比較しています。 - もし結果が期待と異なる場合、
t.Errorf
を使ってエラーメッセージを出力し、テストの失敗を報告します。
このテストコードは、Dir
関数が様々な入力に対して正しく動作することを保証するための重要な役割を担っています。
関連リンク
- Go CL 5503067: https://golang.org/cl/5503067
参考にした情報源リンク
- Go言語
path/filepath
パッケージ公式ドキュメント: https://pkg.go.dev/path/filepath - Go言語
os
パッケージ公式ドキュメント (os.IsPathSeparator): https://pkg.go.dev/os#IsPathSeparator