filepath:处理文件路径的一把好手

1.ToSlash(path string) string

将相关平台的路径分隔符转为/

package main

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

func main() {
    sep := os.PathSeparator

    // 查看当前平台的系统路径分隔符,windows平台是\
    fmt.Println(string(sep))  // \

    // 将分割符转为/
    fmt.Println(filepath.ToSlash(`C:\python37\python.exe`))  // C:/python37/python.exe

    // 注意:该函数不在意路径是否存在,只是当成普通的字符串进行处理
    // 比如我输入一个不存在的路径也是可以的
    fmt.Println(filepath.ToSlash(`C:\python37\python.exe\python.exe`))  // C:/python37/python.exe/python.exe
}

2.FromSlash(path string) string

和ToSlash相反,是将/转化为相关系统的路径分隔符

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    fmt.Println(filepath.FromSlash(`C:/python37/python.exe`))  // C:\python37\python.exe
    fmt.Println(filepath.FromSlash(`C:\python37/python.exe`))  // C:\python37\python.exe
    fmt.Println(filepath.FromSlash(`C:\python37\python.exe`))  // C:\python37\python.exe\\\\

    /*
    可以看到,这两个函数只是简单的字符串替换罢了,在源码里面也是这么做的
    func FromSlash(path string) string {
        if Separator == '/' {
            return path
        }
        return strings.ReplaceAll(path, "/", string(Separator))
    }
    另外,这两个函数在linux下面也能用,但是没有意义,因为linux的路径分隔符就是/
     */
}

3.Dir(path string) string

获取path中最后一个分割符前面的部分,类似于python中的os.path.dirname(path)

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    path := `c:\python37\python.exe`
    fmt.Println(filepath.Dir(path))  // c:\python37
}

4.Base(path string) string

获取path中最后一个分割符后面的部分,类似于python中的os.path.basename(path)

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    path := `c:\python37\python.exe`
    fmt.Println(filepath.Base(path))  // python.exe
}

5.Split(path string) (dir, file string)

相当于是Dir和Base的组合,得到最后一个分割符的前面和后面两部分,类似于python中os.path.split

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    path := `c:\python37\python.exe`
    dir, file := filepath.Split(path)
    fmt.Println(dir, file) // c:\python37\ python.exe
}

6.Ext(path string) string

获取path中的文件扩展名,类似于python中的os.path.splitext(path)[1]

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    path := `c:\python37\python.exe`
    fmt.Println(filepath.Ext(path))  // .exe
}

7.Rel(basepath, targpath string) (string, error)

获取targpath相对于basepath的路径,怎么理解呢?就是basepath进行什么样的操作,才能得到targpath。其中两者必须都是相对路径或者绝对路径

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    path1 := `c:\python37\python.exe`
    path2 := `c:\python37`
    p1, err1 := filepath.Rel(path1, path2)
    if err1 != nil {
        fmt.Println(err1)
    }else{
        fmt.Println(p1)  // ..
    }
    //得到的结果是..,表示path1组合..就能得到path2

    
    path1 = `c:\python37`
    path2 = `c:\python37\python.exe`
    p2, err2 := filepath.Rel(path1, path2)
    if err2 != nil {
        fmt.Println(err2)
    }else{
        fmt.Println(p2)  // python.exe
    }
    // 表示path1再组合python.exe就能得到path2

    
    path1 = `c:\python37`
    path2 = `c:\go`
    p3, err3 := filepath.Rel(path1, path2)
    if err3 != nil {
        fmt.Println(err3)
    }else{
        fmt.Println(p3) // ..\go
    }
    

    path1 = `c:\python37`
    path2 = `d:\overwatch`
    p4, err4 := filepath.Rel(path1, path2)
    if err4 != nil {
        fmt.Println(err4) // Rel: can't make d:\overwatch relative to c:\python37
    }else {
        fmt.Println(p4)
    }
    //在window上,如果跨越了盘符,是没有办法的
    
}

8.Join(elem ...string) string

将多个路径进行组合

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    path1 := `c:\python37`
    path2 := `lib`
    path3 := `site-packages`
    path4 := `tornado`
    fmt.Println(filepath.Join(path1, path2, path3, path4))  // c:\python37\lib\site-packages\tornado

    fmt.Println(filepath.Join(path1, path2, "scripts", "pip"))  // c:\python37\lib\scripts\pip
    fmt.Println(filepath.Join(path1, path2, "..", "scripts", "pip"))  // c:\python37\scripts\pip
    // ..相当于上一层目录
}

9.Clean(path string) string

清理路径中的多余字符

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    path := `c:\python\\\python37\..\\\python.ext\\\aaa\.\::::\\xx`
    fmt.Println(filepath.Clean(path))  // :\python\python.ext\aaa\::::\xx
    // 清除的主要是\\\和\\, 都换成了\,个人觉得不是很常用
}

10.Abs(path string) (string, error)

获取path的绝对路径,类似于python中的os.path.abspath

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    path1 := `.`
    abs_path1, err1 := filepath.Abs(path1)
    if err1 != nil {
        fmt.Println(err1)
    }else {
        fmt.Println(abs_path1)  // D:\komeijisatori
    }
    
    //如果是点的话,那么默认是当前go文件所在的工作区的目录
}

11.IsAbs(path string) bool

判断当前路径是否是绝对路径

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    path1 := `.`
    flag := filepath.IsAbs(path1)
    fmt.Println(flag)  // false

    fmt.Println(filepath.IsAbs(`c:\python37\python.exe`)) // true
}

12.VolumeName(path string) string

返回path所在的卷名,比如c:\python37\python.exe就是c:,而linux中/opt/python/bin就是/opt/python

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    path1 := `c:\python37\python.exe`
    fmt.Println(filepath.VolumeName(path1))  // c:
}

13.EvalSymlinks(path string) (string, error)

返回当前链接(快捷方式)所指向的文件

14.Match(pattern, name string) (matched bool, err error)

判断 name 是否和指定的模式 pattern 完全匹配,有点类似于python中的fnmatch,但是匹配的方式不太一样

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    /*
    ?:匹配单个任意字符
    *:匹配任意个任意字符
    []:匹配某个范围内的任意一个字符
    [^]:匹配某个范围外的任意一个字符
    [a-z]:也可以使用类似于正则表达式的模式,使用-表示一个区间
    \:用来进行转义,匹配实际的字符,比如\*表示*, \[匹配[,但是?是不需要转义的,因为?表示匹配任意一个字符,所以也包含?
    []里面也可以直接包含[ ? *等特殊字符,无需转义。但是] -这些字符则必须要转义,必须是\]或者\-才能匹配]和-
     */
    fmt.Println(filepath.Match(`???`, "abc"))  // true <nil>
    fmt.Println(filepath.Match(`???`, "???"))  // true <nil>
    fmt.Println(filepath.Match(`???`, "abcd"))  // false <nil>

    fmt.Println(filepath.Match(`*`, "abc"))  // true <nil>
    fmt.Println(filepath.Match(`*`, ""))  // true <nil>

    fmt.Println(filepath.Match(`???[?`, "abc[e"))  // false syntax error in pattern

    fmt.Println(filepath.Match(`[aA][bB][cC]`, `aBc`))  // true <nil>
    fmt.Println(filepath.Match(`[^aA]*`,`abc`))  // false <nil>
    fmt.Println(filepath.Match(`[a-z]*`,`a+b`))  // true <nil>

}

15.Glob(pattern string) (matches []string, err error)

列出与指定的模式 pattern 完全匹配的文件或目录(匹配原则同Match)

package main

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

func main() {
    list, err := filepath.Glob(`c:\python37\*`)
    if err != nil{
        fmt.Println(err)
    }else {
        for _, v := range list{
            fmt.Println(v)
            /*
            c:\python37\DLLs
            c:\python37\Doc
            c:\python37\LICENSE.txt
            c:\python37\Lib
            c:\python37\NEWS.txt
            c:\python37\Scripts
            c:\python37\Tools
            c:\python37\cx_Oracle-doc
            c:\python37\etc
            c:\python37\include
            c:\python37\libs
            c:\python37\man
            c:\python37\python.exe
            c:\python37\python3.dll
            c:\python37\python37.dll
            c:\python37\pythonw.exe
            c:\python37\share
            c:\python37\tcl
            c:\python37\vcruntime140.dll
             */
        }
    }

    //遍历文件还有另一种方式
    fileinfo, err := ioutil.ReadDir(`c:\python37`)
    if err != nil {
        fmt.Println(err)
    }else {
        // fileinfo则是一个切片,里面的数据类型是os.FileInfo
        /*
        type FileInfo interface {
            Name() string       // base name of the file
            Size() int64        // length in bytes for regular files; system-dependent for others
            Mode() FileMode     // file mode bits
            ModTime() time.Time // modification time
            IsDir() bool        // abbreviation for Mode().IsDir()
            Sys() interface{}   // underlying data source (can return nil)
        }
         */
        for _, v := range fileinfo{
            message := fmt.Sprintf("文件名:%s,大小:%d,是否是目录,%t", v.Name(), v.Size(), v.IsDir())
            fmt.Println(message)
            /*
            文件名:DLLs,大小:0,是否是目录,true
            文件名:Doc,大小:0,是否是目录,true
            文件名:LICENSE.txt,大小:30195,是否是目录,false
            文件名:Lib,大小:0,是否是目录,true
            文件名:NEWS.txt,大小:665470,是否是目录,false
            文件名:Scripts,大小:0,是否是目录,true
            文件名:Tools,大小:0,是否是目录,true
            文件名:cx_Oracle-doc,大小:0,是否是目录,true
            文件名:etc,大小:0,是否是目录,true
            文件名:include,大小:0,是否是目录,true
            文件名:libs,大小:0,是否是目录,true
            文件名:man,大小:0,是否是目录,true
            文件名:python.exe,大小:99856,是否是目录,false
            文件名:python3.dll,大小:58896,是否是目录,false
            文件名:python37.dll,大小:3748368,是否是目录,false
            文件名:pythonw.exe,大小:98320,是否是目录,false
            文件名:share,大小:0,是否是目录,true
            文件名:tcl,大小:0,是否是目录,true
            文件名:vcruntime140.dll,大小:89752,是否是目录,false
             */
        }
    }
}

16.Walk(root string, walkFn WalkFunc) error

遍历指定目录下的所有子目录,对遍历的项目使用walkFn进行处理,WalkFunc类型如下。

type WalkFunc func(path string, info os.FileInfo, err error) error

这是最后一个例子,我们多写几个例子 

package main

import (
    "fmt"
    "os"
    "path/filepath"
    "strings"
)

func main() {
    walkFn := func(path string, info os.FileInfo, err error) error {
        //遍历的时候,会自动将path:文件路径、info:文件信息、err:错误传递进来,我们可以写上自己的逻辑
        //获取文件名
        filename := info.Name()
        if strings.HasSuffix(filename, "__init__.py"){
            //如果文件是以__init__.py结尾的话, 直接打印对应的path
            fmt.Println(path)
        }
        //这里我们直接返回nil即可
        return nil
    }

    //那么开始遍历,会对遍历的每一个内容都交给walkFn进行处理
    err := filepath.Walk(`c:\python37\lib\site-packages\numpy`, walkFn)
    if err != nil {
        fmt.Println("err =", err)
    }
    /*
    输出结果
    c:\python37\lib\site-packages\numpy\__init__.py
    c:\python37\lib\site-packages\numpy\compat\__init__.py
    c:\python37\lib\site-packages\numpy\compat\tests\__init__.py
    c:\python37\lib\site-packages\numpy\core\__init__.py
    c:\python37\lib\site-packages\numpy\core\tests\__init__.py
    c:\python37\lib\site-packages\numpy\distutils\__init__.py
    c:\python37\lib\site-packages\numpy\distutils\command\__init__.py
    c:\python37\lib\site-packages\numpy\distutils\fcompiler\__init__.py
    c:\python37\lib\site-packages\numpy\distutils\tests\__init__.py
    c:\python37\lib\site-packages\numpy\doc\__init__.py
    c:\python37\lib\site-packages\numpy\f2py\__init__.py
    c:\python37\lib\site-packages\numpy\f2py\tests\__init__.py
    c:\python37\lib\site-packages\numpy\fft\__init__.py
    c:\python37\lib\site-packages\numpy\fft\tests\__init__.py
    c:\python37\lib\site-packages\numpy\lib\__init__.py
    c:\python37\lib\site-packages\numpy\lib\tests\__init__.py
    c:\python37\lib\site-packages\numpy\linalg\__init__.py
    c:\python37\lib\site-packages\numpy\linalg\tests\__init__.py
    c:\python37\lib\site-packages\numpy\ma\__init__.py
    c:\python37\lib\site-packages\numpy\ma\tests\__init__.py
    c:\python37\lib\site-packages\numpy\matrixlib\__init__.py
    c:\python37\lib\site-packages\numpy\matrixlib\tests\__init__.py
    c:\python37\lib\site-packages\numpy\polynomial\__init__.py
    c:\python37\lib\site-packages\numpy\polynomial\tests\__init__.py
    c:\python37\lib\site-packages\numpy\random\__init__.py
    c:\python37\lib\site-packages\numpy\random\tests\__init__.py
    c:\python37\lib\site-packages\numpy\testing\__init__.py
    c:\python37\lib\site-packages\numpy\testing\_private\__init__.py
    c:\python37\lib\site-packages\numpy\testing\tests\__init__.py
    c:\python37\lib\site-packages\numpy\tests\__init__.py
     */
}

统计一下文件个数,分为两个版本,golang版和python版

  • golang

    package main
    
    import (
      "fmt"
      "os"
      "path/filepath"
      "strings"
      "time"
    )
    
    func main() {
      start_time := time.Now().UnixNano()
      //下面我们来统计一下,c:\python37下面有多少个__init__.py文件,有多少个py文件,有多少个不是py文件的文件,有多少个目录
      init_py_file := 0
      py_file := 0
      not_py_file := 0
      dir := 0
    
      walkFn := func(path string, info os.FileInfo, err error) error {
          filename := info.Name()
          is_dir := info.IsDir()
          if strings.HasSuffix(filename, "__init__.py") {
              //如果文件是以__init__.py结尾的话, 别忘了py_file也要++
              init_py_file++
              py_file++
          } else if strings.HasSuffix(filename, ".py") {
              //以.py结尾的话
              py_file++
          } else if is_dir {
              //如果是目录的话
              dir++
          } else {
              //既不是py结尾,也不是目录的话
              not_py_file++
          }
          //这里我们直接返回nil即可
          return nil
      }
    
      //那么开始遍历,会对遍历的每一个内容都交给walkFn进行处理
      err := filepath.Walk(`c:\python37`, walkFn)
      if err != nil {
          fmt.Println("err =", err)
      }
      //打印相应的个数
      fmt.Println(fmt.Sprintf("__init__.py文件个数:%d,py文件个数:%d,不是py文件的文件个数:%d,目录个数:%d", init_py_file, py_file, not_py_file, dir))
      /*
        __init__.py文件个数:1965,py文件个数:11911,不是py文件的文件个数:23050,目录个数:7685
       */
      end_time := time.Now().UnixNano()
      fmt.Println("总用时:", (end_time - start_time) / 1e9) // 总用时: 4
    }
  • python

    from pathlib import Path
    import os
    import time
    
    start_time = time.perf_counter()
    init_py_file = 0
    py_file = 0
    not_py_file = 0
    dir = 0
    p = Path(r"c:\python37")
    for file in p.rglob("*"):
        if str(file).endswith("__init__.py"):
            init_py_file += 1
            py_file += 1
        elif str(file).endswith(".py"):
            py_file += 1
        elif os.path.isdir(str(file)):
            dir += 1
        else:
           not_py_file += 1
    
    end_time = time.perf_counter()
    print(f"__init__.py文件个数:{init_py_file},py文件个数:{py_file},不是py文件的文件个数:{not_py_file},目录个数:{dir}")
    # __init__.py文件个数:1965,py文件个数:11911,不是py文件的文件个数:23050,目录个数:7684
    print(f"总用时:{end_time - start_time}")
    # 总用时:3.695507357
  • 总结:两者之间效率基本上是一样的,代码量的话,由于golang的{}多占了一行,并且如果不考虑if err != nil之类的话,代码量也基本差不多。但是我们比较一下结果的话,其他的是一样的,只有目录的个数不一样,golang的结果貌似比python多了一个,这是因为,我们递归遍历的是c:\python37,在golang中,把c:\python37这个目录本身也给算进去了,而python没有。所以其他的结果是一致的,但是目录个数的话,golang算出来会比python多一个。当然仔细观察结果的话,稍加思考就能发现原因,这里程序就不再演示了,可以找一个文件或目录数量比较少的打印一下,会发现python没有把最外层的目录打印出来,打印都是指定目录里面的内容。

package main

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

func main() {
    walkFn := func(path string, info os.FileInfo, err error) error {
        is_dir := info.IsDir()
        if is_dir{
            //如果是目录,那么我们就跳过,不做处理
            fmt.Println(fmt.Sprintf("%s是目录,已经跳过", path))
            return filepath.SkipDir
        }
        return nil
    }
    //那么开始遍历,会对遍历的每一个内容都交给walkFn进行处理
    err := filepath.Walk(`c:\python37`, walkFn)
    if err != nil {
        fmt.Println("err =", err)
    }
    /*
    c:\python37是目录,已经跳过
     */
    //可以发现,上来就跳过了,跳过是指整个目录跳过,目录里面有什么也不再关心了
    //也可以得出,golang是会处理指定的目录,而python不会,python只处理指定目录里面的内容
}
package main

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

func main() {
    walkFn := func(path string, info os.FileInfo, err error) error {
        filename := info.Name()
        is_dir := info.IsDir()
        if is_dir && filename == "scripts"{
            //如果是目录,并且目录名为scripts,那么我们就跳过,不做处理
            fmt.Println(fmt.Sprintf("%s是目录,已经跳过", path))
            return filepath.SkipDir
        }
        return nil
    }
    //那么开始遍历,会对遍历的每一个内容都交给walkFn进行处理
    err := filepath.Walk(`c:\python37`, walkFn)
    if err != nil {
        fmt.Println("err =", err)
    }
    /*
    c:\python37\Lib\venv\scripts是目录,已经跳过
    c:\python37\Tools\scripts是目录,已经跳过
     */
}

转载于:https://www.cnblogs.com/traditional/p/11409205.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值