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是目录,已经跳过 */ }