Go path/filepath文件路徑操作


本文:https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter06/06.2.html

path:https://www.php.cn/manual/view/35279.html

filepath:https://www.php.cn/manual/view/35280.html

path/filepath — 兼容操作系統的文件路徑操作

path/filepath 包涉及到路徑操作時,路徑分隔符使用 os.PathSeparator。不同系統,路徑表示方式有所不同,比如 Unix 和 Windows 差別很大。本包能夠處理所有的文件路徑,不管是什么系統。

注意,路徑操作函數並不會校驗路徑是否真實存在。

解析路徑名字符串

Dir() 和 Base() 函數將一個路徑名字符串分解成目錄和文件名兩部分。(注意一般情況,這些函數與 Unix 中 dirname 和 basename 命令類似,但如果路徑以 / 結尾,Dir 的行為和 dirname 不太一致。)

func Dir(path string) string func Base(path string) string 

Dir 返回路徑中除去最后一個路徑元素的部分,即該路徑最后一個元素所在的目錄。在使用 Split 去掉最后一個元素后,會簡化路徑並去掉末尾的斜杠。如果路徑是空字符串,會返回 ".";如果路徑由 1 到多個斜杠后跟 0 到多個非斜杠字符組成,會返回 "/";其他任何情況下都不會返回以斜杠結尾的路徑。

Base 函數返回路徑的最后一個元素。在提取元素前會去掉末尾的斜杠。如果路徑是 "",會返回 ".";如果路徑是只有一個斜桿構成的,會返回 "/"。

比如,給定路徑名 /home/polaris/studygolang.goDir 返回 /home/polaris,而 Base 返回 studygolang.go

如果給定路徑名 /home/polaris/studygolang/Dir 返回 /home/polaris/studygolang(這與 Unix 中的 dirname 不一致,dirname 會返回 /home/polaris),而 Base 返回 studygolang

有人提出此問題,見issue13199,不過官方認為這不是問題,如果需要和 dirname 一樣的功能,應該自己處理,比如在調用 Dir 之前,先將末尾的 / 去掉。

此外,Ext 可以獲得路徑中文件名的擴展名。

func Ext(path string) string

Ext 函數返回 path 文件擴展名。擴展名是路徑中最后一個從 . 開始的部分,包括 .。如果該元素沒有 . 會返回空字符串。

相對路徑和絕對路徑

某個進程都會有當前工作目錄(進程相關章節會詳細介紹),一般的相對路徑,就是針對進程當前工作目錄而言的。當然,可以針對某個目錄指定相對路徑。

絕對路徑,在 Unix 中,以 / 開始;在 Windows 下以某個盤符開始,比如 C:\Program Files

func IsAbs(path string) bool

IsAbs 返回路徑是否是一個絕對路徑。而

func Abs(path string) (string, error)

Abs 函數返回 path 代表的絕對路徑,如果 path 不是絕對路徑,會加入當前工作目錄以使之成為絕對路徑。因為硬鏈接的存在,不能保證返回的絕對路徑是唯一指向該地址的絕對路徑。在 os.Getwd 出錯時,Abs 會返回該錯誤,一般不會出錯,如果路徑名長度超過系統限制,則會報錯。

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

Rel 函數返回一個相對路徑,將 basepath 和該路徑用路徑分隔符連起來的新路徑在詞法上等價於 targpath。也就是說,Join(basepath, Rel(basepath, targpath)) 等價於 targpath。如果成功執行,返回值總是相對於 basepath 的,即使 basepath 和 targpath 沒有共享的路徑元素。如果兩個參數一個是相對路徑而另一個是絕對路徑,或者 targpath 無法表示為相對於 basepath 的路徑,將返回錯誤。

fmt.Println(filepath.Rel("/home/polaris/studygolang", "/home/polaris/studygolang/src/logic/topic.go")) fmt.Println(filepath.Rel("/home/polaris/studygolang", "/data/studygolang")) // Output: // src/logic/topic.go <nil> // ../../../data/studygolang <nil> 

路徑的切分和拼接

對於一個常規文件路徑,我們可以通過 Split 函數得到它的目錄路徑和文件名:

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

Split 函數根據最后一個路徑分隔符將路徑 path 分隔為目錄和文件名兩部分(dir 和 file)。如果路徑中沒有路徑分隔符,函數返回值 dir 為空字符串,file 等於 path;反之,如果路徑中最后一個字符是 /,則 dir 等於 pathfile 為空字符串。返回值滿足 path == dir+filedir 非空時,最后一個字符總是 /

// dir == /home/polaris/,file == studygolang filepath.Split("/home/polaris/studygolang") // dir == /home/polaris/studygolang/,file == "" filepath.Split("/home/polaris/studygolang/") // dir == "",file == studygolang filepath.Split("studygolang") 

相對路徑到絕對路徑的轉變,需要經過路徑的拼接。Join 用於將多個路徑拼接起來,會根據情況添加路徑分隔符。

func Join(elem ...string) string

Join 函數可以將任意數量的路徑元素放入一個單一路徑里,會根據需要添加路徑分隔符。結果是經過 Clean 的,所有的空字符串元素會被忽略。對於拼接路徑的需求,我們應該總是使用 Join 函數來處理。

有時,我們需要分割 PATH 或 GOPATH 之類的環境變量(這些路徑被特定於 OS 的列表分隔符連接起來),filepath.SplitList 就是這個用途:

func SplitList(path string) []string

注意,與 strings.Split 函數的不同之處是:對 "",SplitList 返回[]string{},而 strings.Split 返回 []string{""}。SplitList 內部調用的是 strings.Split

規整化路徑

func Clean(path string) string

Clean 函數通過單純的詞法操作返回和 path 代表同一地址的最短路徑。

它會不斷的依次應用如下的規則,直到不能再進行任何處理:

  1. 將連續的多個路徑分隔符替換為單個路徑分隔符
  2. 剔除每一個 . 路徑名元素(代表當前目錄)
  3. 剔除每一個路徑內的 .. 路徑名元素(代表父目錄)和它前面的非 .. 路徑名元素
  4. 剔除開始於根路徑的 .. 路徑名元素,即將路徑開始處的 /.. 替換為 /(假設路徑分隔符是 /

返回的路徑只有其代表一個根地址時才以路徑分隔符結尾,如 Unix 的 / 或 Windows 的 C:\

如果處理的結果是空字符串,Clean 會返回 .,代表當前路徑。

符號鏈接指向的路徑名

在上一節 os 包中介紹了 Readlink,可以讀取符號鏈接指向的路徑名。不過,如果原路徑中又包含符號鏈接,Readlink 卻不會解析出來。filepath.EvalSymlinks 會將所有路徑的符號鏈接都解析出來。除此之外,它返回的路徑,是直接可訪問的。

func EvalSymlinks(path string) (string, error)

如果 path 或返回值是相對路徑,則是相對於進程當前工作目錄。

os.Readlink 和 filepath.EvalSymlinks 區別示例程序:

// 在當前目錄下創建一個 studygolang.txt 文件和一個 symlink 目錄,在 symlink 目錄下對 studygolang.txt 建一個符號鏈接 studygolang.txt.2 fmt.Println(filepath.EvalSymlinks("symlink/studygolang.txt.2")) fmt.Println(os.Readlink("symlink/studygolang.txt.2")) // Ouput: // studygolang.txt <nil> // ../studygolang.txt <nil> 

文件路徑匹配

func Match(pattern, name string) (matched bool, err error)

Match 指示 name 是否和 shell 的文件模式匹配。模式語法如下:

pattern:
    { term }
term:
    '*' 匹配 0 或多個非路徑分隔符的字符 '?' 匹配 1 個非路徑分隔符的字符 '[' [ '^' ] { character-range } ']' 字符組(必須非空) c 匹配字符 c(c != '*', '?', '\\', '[') '\\' c 匹配字符 c character-range: c 匹配字符 c(c != '\\', '-', ']') '\\' c 匹配字符 c lo '-' hi 匹配區間[lo, hi]內的字符 

匹配要求 pattern 必須和 name 全匹配上,不只是子串。在 Windows 下轉義字符被禁用。

Match 函數很少使用,搜索了一遍,標准庫沒有用到這個函數。而 Glob 函數在模板標准庫中被用到了。

func Glob(pattern string) (matches []string, err error)

Glob 函數返回所有匹配了 模式字符串 pattern 的文件列表或者 nil(如果沒有匹配的文件)。pattern的語法和 Match 函數相同。pattern 可以描述多層的名字,如 /usr/*/bin/ed(假設路徑分隔符是 /)。

注意,Glob 會忽略任何文件系統相關的錯誤,如讀目錄引發的 I/O 錯誤。唯一的錯誤和 Match 一樣,在 pattern 不合法時,返回 filepath.ErrBadPattern。返回的結果是根據文件名字典順序進行了排序的。

Glob 的常見用法,是讀取某個目錄下所有的文件,比如寫單元測試時,讀取 testdata 目錄下所有測試數據:

filepath.Glob("testdata/*.input")

遍歷目錄

在介紹 os 時,講解了讀取目錄的方法,並給出了一個遍歷目錄的示例。在 filepath 中,提供了 Walk函數,用於遍歷目錄樹。

func Walk(root string, walkFn WalkFunc) error

Walk 函數會遍歷 root 指定的目錄下的文件樹,對每一個該文件樹中的目錄和文件都會調用 walkFn,包括 root 自身。所有訪問文件 / 目錄時遇到的錯誤都會傳遞給 walkFn 過濾。文件是按字典順序遍歷的,這讓輸出更漂亮,但也導致處理非常大的目錄時效率會降低。Walk 函數不會遍歷文件樹中的符號鏈接(快捷方式)文件包含的路徑。

walkFn 的類型 WalkFunc 的定義如下:

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

Walk 函數對每一個文件 / 目錄都會調用 WalkFunc 函數類型值。調用時 path 參數會包含 Walk 的 root 參數作為前綴;就是說,如果 Walk 函數的 root 為 "dir",該目錄下有文件 "a",將會使用 "dir/a" 作為調用 walkFn 的參數。walkFn 參數被調用時的 info 參數是 path 指定的地址(文件 / 目錄)的文件信息,類型為 os.FileInfo

如果遍歷 path 指定的文件或目錄時出現了問題,傳入的參數 err 會描述該問題,WalkFunc 類型函數可以決定如何去處理該錯誤(Walk 函數將不會深入該目錄);如果該函數返回一個錯誤,Walk 函數的執行會中止;只有一個例外,如果 Walk 的 walkFn 返回值是 SkipDir,將會跳過該目錄的內容而 Walk 函數照常執行處理下一個文件。

和 os 遍歷目錄樹的示例對應,使用 Walk 遍歷目錄樹的示例程序在 walk,程序簡單很多。

Windows 起作用的函數

filepath 中有三個函數:VolumeNameFromSlash 和 ToSlash,針對非 Unix 平台的。

關於 path 包

path 包提供了對 / 分隔的路徑的實用操作函數。

在 Unix 中,路徑的分隔符是 /,但 Windows 是 \。在使用 path 包時,應該總是使用 /,不論什么系統。

path 包中提供的函數,filepath 都有提供,功能類似,但實現不同。

一般應該總是使用 filepath 包,而不是 path 包。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM