kurumi-bioの雑記帳

プログラミング、パソコン、ペット、 犬、お出かけ

初心者のGo言語 -46- <Rel,Split,SplitList,ToSlash>

こんにちは、kurumi-bioです。
第4回目のfilepathパッケージ(標準ライブラリー)の学習です。

前回の記事

kurumi-bio.hatenablog.com

APIリファレンス(過去記事の一覧)

kurumi-bio.hatenablog.com

環境

  • Windows
    OSバージョン:Windows11 Home 22H2
    Go言語のバージョン:go version go1.20.3 windows/amd64
  • Linux
    OSバージョン:openSUSE Leap 15.4
    Go言語のバージョン:go version go1.20.3 linux/amd64

コードの説明

ファイルパス文字列の操作を行う関数ですので、色々な形式のパスを渡して結果を確認してみました。

Rel関数

func Rel(basepath, targpath string) (string, error)

関数の説明 Rel は、区切り文字を介して Basepath に結合されると、字句的に targpath と同等の相対パスを返します。
つまり、Join(basepath, Rel(basepath, targpath)) は targpath 自体と同等です。
成功すると、basepath と targpath が要素を共有していない場合でも、返されるパスは常に Basepath に対する相対パスになります。
targpath をベースパスに対して相対的に作成できない場合、またはそれを計算するために現在の作業ディレクトリが必要であることがわかっている場合は、エラーが返されます。
Rel は結果に対して Clean を呼び出します。

package main

import (
    "fmt"
    "path/filepath"
)

func printResult(b, t string) {
    s, e := filepath.Rel(b, t)

    if e != nil {
        fmt.Printf("Rel Error [%v] : basepath=[%s]/ targpath=[%s]\n", e, b, t)
    } else {
        fmt.Printf("\tbasepath=[%-20s] targpath=[%-30s] -> [%-20s]\n", b, t, s)
    }
}

func main() {
    println("◆ドライブ名で始まる(Windows形式)")
    //BasepathがTargetPathに含まれる場合
    printResult("c:\\folder1", "c:\\folder1\\file1")
    //BasepathがTargetPathに含まれない場合
    printResult("c:\\folder1", "c:\\folder2\\file2")
    //BasepathがTargetPathに含まれない場合
    printResult("c:\\folder1\\folder2", "c:\\folder3\\folder4\\file5")

    println("◆パス区切り文字で始まる(Linux形式)")
    //BasepathがTargetPathに含まれる場合
    printResult("/folder1", "/folder1/file1")
    //BasepathがBasepathに含まれない場合
    printResult("/folder1", "/folder2/file2")
    //BasepathがTargetPathに含まれない場合
    printResult("/folder1/folder2", "/folder3/folder4/file5")

    println("◆パス区切り文字で始まらない")
    printResult("folder1", "/folder1/file1")
    printResult("/folder1", "folder1/file1")
    printResult("folder1", "folder1/file1")
    printResult("folder1", "folder2/file1")
}

◆実行結果(Windows)

◆実行結果(Linux)

実行結果の説明 Basepathに対するTargetPathの相対パスを返すので、
TargetPathとBasepathが同じ文字列(階層)が除去されます。

TargetPathとBasepathが異なる文字列の場合は、差分だけ親パスに戻る記述(../)に書き換わります。

文字列操作ですので、実際にフォルダ、ファイルが存在しなくてもエラーになりませんでした。

Linux環境はドライブ名を解釈しないため別パスと判断して親パスに戻ってしましました。
それ以外は、Windows環境とLinux環境で、同じ結果でした。

Split関数

func Split(path string) (dir, file string)

関数の説明 Split は、最後のセパレータの直後でパスを分割し、ディレクトリとファイル名のコンポーネントに分割します。
パスにセパレータがない場合、Split は空のディレクトリとパスに設定されたファイルを返します。
戻り値には、path = dir+file というプロパティがあります。

package main

import (
    "fmt"
    "path/filepath"
)

func printResult(p string) {
    d, f := filepath.Split(p)
    fmt.Printf("\tpath=[%-20s] -> dir=[%-20s] file=[%-10s]\n", p, d, f)
}

func main() {
    println("◆ドライブ名で始まる(Windows形式)")
    printResult("c:/file")
    printResult("c:/folder/file")

    println("◆パス区切り文字で始まる(Linux形式)")
    printResult("/folder")
    printResult("/folder/file")

    println("◆パス区切り文字で始まらない")
    printResult("folder1/folder2/file")
    printResult("file")
}

◆実行結果(Windows)

◆実行結果(Linux)

実行結果の説明 Windows環境とLinux環境で同じ結果になりました。
この関数も文字列操作ですので、実際にフォルダ、ファイルが存在しなくてもエラーになりませんでした。

SplitList関数

func SplitList(path string) []string

関数の説明 SplitList は、OS 固有の ListSeparator (通常は PATH または GOPATH 環境変数にあります) によって結合されたパスのリストを分割します。
strings.Split とは異なり、SplitList は空の文字列が渡されると空のスライスを返します。

package main

import (
    "fmt"
    "path/filepath"
)

func printResult(p string) {
    fmt.Printf("\t%s\n", p)
    l := filepath.SplitList(p)
    for i, v := range l {
        fmt.Printf("\t\tvalue(%d)=[%s]\n", i, v)
    }
}

func main() {
    println("◆ドライブ名で始まる(Windows形式)")
    printResult("c:\\file")
    printResult("c:\\folder\\file;d:\\folder2\\file2")

    println("◆パス区切り文字で始まる(Linux形式)")
    printResult("/folder")
    printResult("/folder/file;/folder2/file2")
    printResult("/folder/file:/folder2/file2")
}

◆実行結果(Windows)

◆実行結果(Linux)

実行結果の説明 Windows環境は、;(セミコロン)で分割されて、Linux環境は、:(コロン)で分割されました。
ですので、Linux環境でパスにドライブ名が含まれていると、戻り値の最初の配列の値がドライブ名になります。
この関数も文字列操作ですので、実際にフォルダ、ファイルが存在しなくてもエラーになりませんでした。

ToSlash関数

func ToSlash(path string) string

関数の説明 ToSlash は、パス内の各区切り文字をスラッシュ (「/」) 文字に置き換えた結果を返します。
複数の区切り文字は複数のスラッシュに置き換えられます。

package main

import (
    "fmt"
    "path/filepath"
)

func printResult(p string) {
    s := filepath.ToSlash(p)
    fmt.Printf("\tpath=[%-20s] -> [%-20s]\n", p, s)
}

func main() {
    println("◆ドライブ名で始まる(Windows形式)")
    printResult("c:\\file")
    printResult("c:\\folder\\file")

    println("◆パス区切り文字で始まる(Linux形式)")
    printResult("/folder")
    printResult("/folder/file")

    println("◆パス区切り文字で始まらない")
    printResult("folder1/folder2/file")
    printResult("file")
}

◆実行結果(Windows)

◆実行結果(Linux)

実行結果の説明 Windows環境とLinux環境の両方ともパス区切り文字が/(スラッシュ)に置き換えられました。
この関数も文字列操作ですので、実際にフォルダ、ファイルが存在しなくてもエラーになりませんでした。

最後に

今回も単純な文字列操作でした。
わざわざ関数が用意されていなくても、stringsパッケージの関数を使えば実装できそうな感じです。
Rel関数が、多少難易度が高そうな感じですし使い道もあるかなぁという感じですが、他の関数は使うことが無さそうです。

最後までご覧いただきありがとうございます