Golangでパスを正規化する

Golangでパスを正規化しようとしてたんですけど、どうも簡単に実装できるらしいことがわかったのでその話。

 

パスの正規化とは

例えばこんなパスを見た時、

hoge/..//./fuga//.//./piyo.txt

普通の人間なら"fuga/piyo.txt"と簡潔な形になおしたくなるはずです。

「パスの正規化」という言葉が正しいのかどうか僕にはよくわからないんですけど、とにかくこの記事では正規化とは、

「"./"とか"../"とか"//"が含まれていたりする冗長なパスを、できるだけ簡潔な状態にすること」

ということにします。

 

Golangでパスの正規化がしたい

ちょっと最近パスの正規化がしたい場面に出くわしたので、最初は普通にこういうのを書いていました。

func PathCanonicalize(path string) string {
splitPath := strings.Split(path, string(os.PathSeparator))
canonicalizedPath := make([]string, 0)
for _, element := range splitPath {
switch element {
case ".", "":
// remove element
case "..":
if len(canonicalizedPath) == 0 || canonicalizedPath[len(canonicalizedPath)-1] == ".." {
canonicalizedPath = append(canonicalizedPath, element)
} else {
canonicalizedPath = canonicalizedPath[:len(canonicalizedPath)-1]
}
default:
canonicalizedPath = append(canonicalizedPath, element)
}
fmt.Println(canonicalizedPath)
}
if filepath.IsAbs(path) {
canonicalizedPath = append([]string{string(os.PathSeparator)}, canonicalizedPath...)
}
return filepath.Join(canonicalizedPath...)
}

きちんとしたverifyは行なっていませんが、これで多分うまく動作します。

ただ実はこれ、こんなにちゃんと実装する必要はないです。

 

filepath.Join()君けっこうやるやんけ

絶対パス以外の場合はstrings.Splitで要素ごとに分割してfilepath.Joinで組み立てるだけで勝手に正規化してくれます。

func PathCanonicalize(path string) string {
splitPath := strings.Split(path, string(os.PathSeparator))
if filepath.IsAbs(path) {
splitPath[0] = "/"
}
return filepath.Join(splitPath...)
}

ただし絶対パスの場合だけ注意が必要で、strings.Split()で先頭要素が空文字列にされてしまうので、"/"を追加する必要があります。

えー、filepath.Join()結構ちゃんとしてるのね。

path.Join()でも同様の使い方ができます。

 

追記

filepath.Clean()っていうそのまんまな関数ありました。