go语言-golang包-path/filepath包

path/filepath
软件包filepath实现了实用程序例程,用于以与目标操作系统定义的文件路径兼容的方式来处理文件名路径。

文件路径包使用正斜杠或反斜杠,具体取决于操作系统。若要处理诸如URL之类的路径,无论使用什么操作系统,该路径始终使用正斜杠,请参阅路径包。
------------------------------------------------------------ 
路径分隔符转换:
const (
Separator = os.PathSeparator // 路径分隔符(分隔路径元素)
ListSeparator = os.PathListSeparator // 路径列表分隔符(分隔多个路径)
)
------------------------------------------------------------ 

Abs

// func Abs(path string) (string, error)
// 返回所给目录的绝对路径
package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    // 返回所给目录的绝对路径
    s1, _ := filepath.Abs("./a/b")
    fmt.Println(s1)
    fmt.Println(filepath.Abs(s1)) // 不同系统显示不一样
    s2 := "/root/a/b/c"
    s3 := "a/b/v/"
    fmt.Println(filepath.IsAbs(s1)) // true
    fmt.Println(filepath.IsAbs(s2)) // true
    fmt.Println(filepath.IsAbs(s3)) // false

}

/*
$ go run Abs.go
/Users/jordan/GolandProjects/LearnGoProject/go-new-course/day06-20200516/filepath/a/b
/Users/jordan/GolandProjects/LearnGoProject/go-new-course/day06-20200516/filepath/a/b <nil>
true
true
false

*/

IsAbs

// 判断路径是否为绝对路径
// IsAbs(path string) bool
package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    fmt.Println("On Unix:")
    fmt.Println("1: ", filepath.IsAbs("/home/gopher"))
    fmt.Println("2: ", filepath.IsAbs(".bashrc"))
    fmt.Println("3: ", filepath.IsAbs(".."))
    fmt.Println("4: ", filepath.IsAbs("."))
    fmt.Println("5: ", filepath.IsAbs("/"))
    fmt.Println("6: ", filepath.IsAbs(""))
}

/*
$ go run IsAbs.go
On Unix:
1:  true
2:  false
3:  false
4:  false
5:  true
6:  false

*/

Base

// func Base(path string) string
// 获取 path 中最后一个分隔符之后的部分(不包含分隔符)

// Split(path string) (dir, file string)
// 获取 path 中最后一个分隔符前后的两部分,之前包含分隔符,之后不包含分隔符

// Ext(path string) string
// 获取路径字符串中的文件扩展名

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    fmt.Println("On Unix:")
    fmt.Println(filepath.Base("/foo/bar/baz.js"))
    fmt.Println(filepath.Base("/foo/bar/baz"))
    fmt.Println(filepath.Base("/foo/bar/baz/"))
    fmt.Println(filepath.Base("dev.txt"))
    fmt.Println(filepath.Base("../todo.txt"))
    fmt.Println(filepath.Base(".."))
    fmt.Println(filepath.Base("."))
    fmt.Println(filepath.Base("/"))
    fmt.Println(filepath.Base(""))
}

/*
$ go run Base.go
On Unix:
baz.js
baz
baz
dev.txt
todo.txt
..
.
/
.
*/

Clean

// 清理路径中的多余字符,比如 /// 或 ../ 或 ./
// Clean(path string) string
package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    // Linux为例
    s := filepath.Clean("a/./b/:::/..//  /c/..////d///")
    fmt.Println("1: ", s)

    //返回等价的最短路径
    //1.用一个斜线替换多个斜线
    fmt.Println("2: ", filepath.Clean("/.../..../////abc/abc"))
    //2.清除当前路径.
    fmt.Println("3: ", filepath.Clean("./1.txt"))
    //3.清除内部的..和他前面的元素
    fmt.Println("4: ", filepath.Clean("C:/a/b/../c"))
    //4.以/..开头的,变成/
    fmt.Println("5: ", filepath.Clean("/../1.txt"))
}

/*
$ go run Clean.go
1:  a/b/  /d
2:  /.../..../abc/abc
3:  1.txt
4:  C:/a/c
5:  /1.txt
*/

Dir

// Dir(path string) string
// 获取 path 中最后一个分隔符之前的部分(不包含分隔符)
package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    fmt.Println("On Unix:")
    fmt.Println(filepath.Dir("/foo/bar/baz.js"))
    fmt.Println(filepath.Dir("/foo/bar/baz"))
    fmt.Println(filepath.Dir("/foo/bar/baz/"))
    fmt.Println(filepath.Dir("/dirty//path///"))
    fmt.Println(filepath.Dir("dev.txt"))
    fmt.Println(filepath.Dir("../todo.txt"))
    fmt.Println(filepath.Dir(".."))
    fmt.Println(filepath.Dir("."))
    fmt.Println(filepath.Dir("/"))
    fmt.Println(filepath.Dir(""))

}

/*
$ go run DIr.go
On Unix:
/foo/bar
/foo/bar
/foo/bar/baz
/dirty/path
.
..
.
.
/
.

*/

Ext

// Ext(path string) string
// 获取路径字符串中的文件扩展名
package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    fmt.Printf("No dots: %q\n", filepath.Ext("index"))
    fmt.Printf("One dot: %q\n", filepath.Ext("index.js"))
    fmt.Printf("Two dots: %q\n", filepath.Ext("main.test.js"))
}

/*
$ go run Ext.go
No dots: ""
One dot: ".js"
Two dots: ".js"

*/

FromSlash-ToSlash

// 下面两个函数主要用于将 Windows 路径分隔符转换为 Linux 路径分隔符,处理完后再转换回去,只在 Windows 中有用,在 Linux 中没必要:
// 将 path 中平台相关的路径分隔符转换为 '/'
// ToSlash(path string) string

// 将 path 中的 '/' 转换为系统相关的路径分隔符
// FromSlash(path string) string

package main

import (
    "fmt"
    "net/url"
    "os"
    "path/filepath"
)

func main() {
    s := "http://www.site.com/a/b/c/d"
    u, _ := url.Parse(s)
    fmt.Println(u)
    s = u.Path
    fmt.Println(s)

    // 下面这句用于 Windows 系统
    s = filepath.FromSlash(s)
    fmt.Println(s)

    // 创建目录试试
    if err := os.MkdirAll(s[1:], 0777); err != nil {
        fmt.Println(err)
    }
    // 下面这句用于 Windows 系统
    s = filepath.ToSlash(s)
    fmt.Println(s)

}

/*
$ go run FromSlash.go
http://www.site.com/a/b/c/d
/a/b/c/d
/a/b/c/d
/a/b/c/d
*/

Glob

// 列出与指定的模式 pattern 完全匹配的文件或目录(匹配原则同match)
// Glob(pattern string) (matches []string, err error)
package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    // 列出 filepath 的子目录中所包含的以 ba(忽略大小写)开头的项目
    list, err := filepath.Glob("/Users/jordan/GolandProjects/LearnGoProject/go-new-course/day06-20200516/*/[Bb]*")
    if err != nil {
        fmt.Println(err)
    }
    for _, v := range list {
        fmt.Println(v)
    }
}

/*
$ go run Glob.go
/Users/jordan/GolandProjects/LearnGoProject/go-new-course/day06-20200516/a/b
/Users/jordan/GolandProjects/LearnGoProject/go-new-course/day06-20200516/filepath/Base.go
/Users/jordan/GolandProjects/LearnGoProject/go-new-course/day06-20200516/test/b.go
/Users/jordan/GolandProjects/LearnGoProject/go-new-course/day06-20200516/test/b.txt

*/

Join

// 将 elem 中的多个元素合并为一个路径,忽略空元素,清理多余字符。
// Join(elem ...string) string

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    fmt.Println("On Unix:")
    fmt.Println("1: ", filepath.Join("a", "b", "c"))
    fmt.Println("2: ", filepath.Join("a", "b/c"))
    fmt.Println("3: ", filepath.Join("a/b", "c"))
    fmt.Println("4: ", filepath.Join("a/b", "/c"))
}

/*
$ go run Join.go
On Unix:
1:  a/b/c
2:  a/b/c
3:  a/b/c
4:  a/b/c

*/

Match

// 判断 name 是否和指定的模式 pattern 完全匹配
// Match(pattern, name string) (matched bool, err error)
// pattern 规则如下:
// 可以使用 ? 匹配单个任意字符(不匹配路径分隔符)。
// 可以使用 * 匹配 0 个或多个任意字符(不匹配路径分隔符)。
// 可以使用 [] 匹配范围内的任意一个字符(可以包含路径分隔符)。
// 可以使用 [^] 匹配范围外的任意一个字符(无需包含路径分隔符)。
// [] 之内可以使用 - 表示一个区间,比如 [a-z] 表示 a-z 之间的任意一个字符。
// 反斜线用来匹配实际的字符,比如 \* 匹配 *,\[ 匹配 [,\a 匹配 a 等等。
// [] 之内可以直接使用 [ * ?,但不能直接使用 ] -,需要用 \]、\- 进行转义。

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    fmt.Println("On Unix:")
    fmt.Println(filepath.Match("/home/catch/*", "/home/catch/foo"))     // true
    fmt.Println(filepath.Match("/home/catch/*", "/home/catch/foo/bar")) // false
    fmt.Println(filepath.Match("/home/?opher", "/home/gopher"))         // true
    fmt.Println(filepath.Match("/home/\\*", "/home/*"))                 // true

    fmt.Println(filepath.Match(`???`, `abc`))          // true
    fmt.Println(filepath.Match(`???`, `abcd`))         // false
    fmt.Println(filepath.Match(`*`, `abc`))            // true
    fmt.Println(filepath.Match(`*`, ``))               // true
    fmt.Println(filepath.Match(`a*`, `abc`))           // true
    fmt.Println(filepath.Match(`???\\???`, `abc\def`)) // true
    fmt.Println(filepath.Match(`???/???`, `abc/def`))  // true
    fmt.Println(filepath.Match(`/*/*/*/`, `/a/b/c/`))  // true
    fmt.Println(filepath.Match(`[aA][bB][cC]`, `aBc`)) // true
    fmt.Println(filepath.Match(`[^aA]*`, `abc`))       // false
    fmt.Println(filepath.Match(`[a-z]*`, `a+b`))       // true
    fmt.Println(filepath.Match(`\[*\]`, `[a+b]`))      // true
    fmt.Println(filepath.Match(`[[\]]*[[\]]`, `[]`))   // true
}

Rel

// 获取 targpath 相对于 basepath 的路径。
// 要求 targpath 和 basepath 必须“都是相对路径”或“都是绝对路径”。
// Rel(basepath, targpath string) (string, error)
package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    //例1:
    paths := []string{
        "/a/b/c",
        "/b/c",
        "./b/c",
    }
    base := "/a"

    fmt.Println("On Unix:")
    for _, p := range paths {
        rel, err := filepath.Rel(base, p)
        fmt.Printf("%q: %q %v\n", p, rel, err)
    }
    /*
        On Unix:
        "/a/b/c": "b/c" <nil>
        "/b/c": "../b/c" <nil>
        "./b/c": "" Rel: can't make ./b/c relative to /a
    */

    //例2:
    // 都是绝对路径
    s, err := filepath.Rel(`/a/b/c`, `/a/b/c/d/e`)
    fmt.Println(s, err) // d/e <nil>

    // 都是相对路径
    s, err = filepath.Rel(`a/b/c`, `a/b/c/d/e`)
    fmt.Println(s, err) // d/e <nil>

    // 一个绝对一个相对
    s, err = filepath.Rel(`/a/b/c`, `a/b/c/d/e`)
    fmt.Println(s, err)
    //  Rel: can't make a/b/c/d/e relative to /a/b/c

    // 一个相对一个绝对
    s, err = filepath.Rel(`a/b/c`, `/a/b/c/d/e`)
    fmt.Println(s, err)
    //  Rel: can't make /a/b/c/d/e relative to a/b/c

    // 从 `a/b/c` 进入 `a/b/d/e`,只需要进入 `../d/e` 即可
    s, err = filepath.Rel(`a/b/c`, `a/b/d/e`)
    fmt.Println(s, err) // ../d/e <nil>
}

Split

// 获取 path 中最后一个分隔符前后的两部分
// 之前包含分隔符,之后不包含分隔符
// Split(path string) (dir, file string)

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    paths := []string{
        "/home/arnie/amelia.jpg",
        "/mnt/photos/",
        "rabbit.jgp",
        "/usr/local//go",
    }
    fmt.Println("On Unix:")
    for _, path := range paths {
        dir, file := filepath.Split(path)
        fmt.Printf("input: %q\n\tdir: %q\n\tfile: %q\n", path, dir, file)
    }
}

/*
$ go run Split.go
On Unix:
input: "/home/arnie/amelia.jpg"
        dir: "/home/arnie/"
        file: "amelia.jpg"
input: "/mnt/photos/"
        dir: "/mnt/photos/"
        file: ""
input: "rabbit.jgp"
        dir: ""
        file: "rabbit.jgp"
input: "/usr/local//go"
        dir: "/usr/local//"
        file: "go"

*/

SplitList

// 将路径序列 操作系统特别的连接符组成的path,通常用在PATH或者GOPARTH环境变量中, 分割为多条独立的路径。跟strings.Split不同,当传入空的字符串,SplitList会返回一个空的切片
// SplitList(path string) []string

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    fmt.Println("On Unix:", filepath.SplitList("/a/b/c:/usr/bin"))
    fmt.Println("On Unix:", filepath.SplitList(""))
}

/*
$ go run SplitList.go
On Unix: [/a/b/c /usr/bin]
On Unix: []

*/

Walk-WalkFunc

// 遍历指定目录(包括子目录),对遍历到的项目用 walkFn 函数进行处理。
// Walk遍历以root为根的文件树,为树中的每个文件或目录(包括root)调用walkFn。
// walkFn会过滤访问文件和目录时出现的所有错误。这些文件以词法顺序进行遍历,这使输出具有确定性,但是这意味着对于非常大的目录,遍历可能效率不高。walk不能找到链接的文件
// Walk(root string, walkFn WalkFunc) error
// 文件处理函数定义如下,如果 WalkFunc 返回 nil,则 Walk 函数继续遍历,如果返回 SkipDir,则 Walk 函数会跳过当前目录(如果当前遍历到的是文件,则同时跳过后续文件及子目录),继续遍历下一个目录。
// 如果返回其它错误,则 Walk 函数会中止遍历过程。
// 在 Walk 遍历过程中,如果遇到错误,则会将错误通过 err 传递给WalkFunc 函数,同时 Walk 会跳过出错的项目,继续处理后续项目。
//type WalkFunc func(path string, info os.FileInfo, err error) error

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "path/filepath"
)

func prepareTestDirTree(tree string) (string, error) {
    tmpDir, err := ioutil.TempDir("", "")
    if err != nil {
        return "", fmt.Errorf("error creating temp directory: %v\n", err)
    }
    err = os.MkdirAll(filepath.Join(tmpDir, tree), 0755)
    if err != nil {
        os.RemoveAll(tmpDir)
        return "", err
    }
    return tmpDir, nil
}

func main() {
    tmpDir, err := prepareTestDirTree("dir/to/walk/skip")
    if err != nil {
        fmt.Printf("unable to create test dir tree: %v\n", err)
        return
    }
    defer os.RemoveAll(tmpDir)
    os.Chdir(tmpDir)

    subDirToSkip := "skip"

    fmt.Println("On Unix: ")
    err = filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
        if err != nil {
            fmt.Printf("prevent panic by handing failure accessing a path %q: %v\n", path, err)
            return err
        }
        if info.IsDir() && info.Name() == subDirToSkip {
            fmt.Printf("skipping a dir without errors: %+v \n", info.Name())
            return filepath.SkipDir
        }
        fmt.Printf("visited file or dir:%q\n", path)
        return nil

    })
    if err != nil {
        fmt.Printf("error walking the path %q: %v\n", tmpDir, err)
        return
    }
}

/*
$ go run Walk.go
On Unix:
visited file or dir:"."
visited file or dir:"dir"
visited file or dir:"dir/to"
visited file or dir:"dir/to/walk"
skipping a dir without errors: skip
*/