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()っていうそのまんまな関数ありました。