[インデックス 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