簡體   English   中英

如何檢查 Go 中是否存在文件?

[英]How to check if a file exists in Go?

Go 的標准庫沒有 function 僅用於檢查文件是否存在(如 Python 的os.path.exists )。 慣用的方法是什么?

要檢查文件是否不存在,等效於Python, if not os.path.exists(filename)

if _, err := os.Stat("/path/to/whatever"); os.IsNotExist(err) {
  // path/to/whatever does not exist
}

檢查文件是否存在,等效於Python的if os.path.exists(filename)

編輯:根據最近的評論

if _, err := os.Stat("/path/to/whatever"); err == nil {
  // path/to/whatever exists

} else if os.IsNotExist(err) {
  // path/to/whatever does *not* exist

} else {
  // Schrodinger: file may or may not exist. See err for details.

  // Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence


}

Caleb Spare的回答發表在gonuts郵件列表中。

[...]實際上並不經常使用,並且使用os.Stat對於需要它的情況os.Stat非常容易。

[...]例如:如果要打開文件,則沒有理由先檢查它是否存在。 檢查和打開之間文件可能會消失,無論如何,無論如何你都需要檢查os.Open錯誤。 因此,在嘗試打開文件后,只需調用os.IsNotExist(err) ,並在那里處理它不存在(如果需要特殊處理)。

[...]您根本不需要檢查存在的路徑(您不應該)。

  • 無論路徑是否已存在, os.MkdirAll正常工作。 (您還需要檢查該呼叫的錯誤。)

  • 您應該使用os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) ,而不是使用os.Create 這樣,如果文件已存在,您將收到錯誤。 此外,沒有其他制作文件的競爭條件,與預先檢查存在的版本不同。

摘自: https//groups.google.com/forum/#! msg / golang-nuts / Ayx-BMNdMFo / 4rL8FFHr8v4J

首先要考慮的是,您很少會只想檢查文件是否存在。 在大多數情況下,如果文件存在,您會嘗試對文件執行某些操作。 在 Go 中,任何時候你試圖對一個不存在的文件執行一些操作,結果應該是一個特定的錯誤( os.ErrNotExist ),最好的辦法是檢查是否返回err值(例如,當調用一個像os.OpenFile(...) ) 這樣的函數是os.ErrNotExist

過去推薦的方法

file, err := os.OpenFile(...)
if os.IsNotExist(err) {
    // handle the case where the file doesn't exist
}

但是,由於在 Go 1.13 (2019 年末發布)中添加了errors.Is新的建議是使用errors.Is

file, err := os.OpenFile(...)
if errors.Is(err, os.ErrNotExist) {
    // handle the case where the file doesn't exist
}

通常最好避免使用os.Stat來檢查文件是否存在,然后再嘗試對其進行處理,因為在您之前的時間窗口中,文件總是有可能被重命名、刪除等用它做點什么。

但是,如果您對這個警告沒有意見,並且您真的,真的只想檢查文件是否存在,然后繼續使用它做一些有用的事情(作為一個人為的例子,假設您正在編寫一個毫無意義的 CLI 工具告訴你一個文件是否存在,然后退出¯\\_(ツ)_/¯ ),那么推薦的方法是:

if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) {
    // file does not exist
} else {
    // file exists
}

您應該使用os.Stat()os.IsNotExist()函數,如以下示例所示:

// Exists reports whether the named file or directory exists.
func Exists(name string) bool {
    if _, err := os.Stat(name); err != nil {
        if os.IsNotExist(err) {
            return false
        }
    }
    return true
}

這個例子是從這里提取的。

user11617示例不正確; 它會報告該文件存在,即使它沒有,但有一些其他類型的錯誤。

簽名應該是Exists(字符串)(bool,錯誤)。 然后,實際上,呼叫站點並沒有更好。

他寫的代碼更好:

func Exists(name string) bool {
    _, err := os.Stat(name)
    return !os.IsNotExist(err)
}

但我建議這樣做:

func Exists(name string) (bool, error) {
  _, err := os.Stat(name)
  if os.IsNotExist(err) {
    return false, nil
  }
  return err != nil, err
}
    _, err := os.Stat(file)
    if err == nil {
        log.Printf("file %s exists", file)
    } else if os.IsNotExist(err) {
        log.Printf("file %s not exists", file)
    } else {
        log.Printf("file %s stat error: %v", file, err)
    }

基本上


package main

import (
    "fmt"
    "os"
)

func fileExists(path string) bool {
    _, err := os.Stat(path)
    return !os.IsNotExist(err)
}

func main() {

    var file string = "foo.txt"
    exist := fileExists(file)
    
    if exist {
        fmt.Println("file exist")
    } else {

        fmt.Println("file not exists")
    }

}

運行示例

另一種方式

使用 os.Open

package main

import (
    "fmt"
    "os"
)

func fileExists(path string) bool {
    _, err := os.Open(path) // For read access.
    return err == nil

}

func main() {

    fmt.Println(fileExists("d4d.txt"))

}


運行

正如其他答案中提到的,可以通過將不同的標志與os.OpenFile一起使用來構建所需的行為/錯誤。 事實上, os.Create只是這樣做的一個明智的默認簡寫:

// Create creates or truncates the named file. If the file already exists,
// it is truncated. If the file does not exist, it is created with mode 0666
// (before umask). If successful, methods on the returned File can
// be used for I/O; the associated file descriptor has mode O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(name string) (*File, error) {
    return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

您應該自己組合這些標志以獲得您感興趣的行為:

// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
    // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
    O_RDONLY int = syscall.O_RDONLY // open the file read-only.
    O_WRONLY int = syscall.O_WRONLY // open the file write-only.
    O_RDWR   int = syscall.O_RDWR   // open the file read-write.
    // The remaining values may be or'ed in to control behavior.
    O_APPEND int = syscall.O_APPEND // append data to the file when writing.
    O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
    O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist.
    O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
    O_TRUNC  int = syscall.O_TRUNC  // truncate regular writable file when opened.
)

根據您選擇的內容,您會得到不同的錯誤。

下面是一個示例,它要么截斷現有文件,要么在文件存在時失敗。

openOpts := os.O_RDWR|os.O_CREATE
if truncateWhenExists {
    openOpts |= os.O_TRUNC // file will be truncated
} else {
    openOpts |= os.O_EXCL  // file must not exist
}
f, err := os.OpenFile(filePath, openOpts, 0644)
// ... do stuff

功能示例:

func file_is_exists(f string) bool {
    _, err := os.Stat(f)
    if os.IsNotExist(err) {
        return false
    }
    return err == nil
}

檢查文件是否存在的最佳方法:

if _, err := os.Stat("/path/to/file"); err == nil || os.IsExist(err) {
    // your code here if file exists
}

這就是我檢查Go 1.16是否存在文件的方式

package main

import (
    "errors"
    "fmt"
    "io/fs"
    "os"
)

func main () {
    if _, err:= os.Stat("/path/to/file"); errors.Is(err, fs.ErrNotExist){
        fmt.Print(err.Error())
    } else {
        fmt.Print("file exists")
    }
}

讓我們首先看幾個方面, golangos包提供的功能golang是實用程序而是錯誤檢查器,我的意思是它們只是一個處理跨平台錯誤的包裝器。

所以基本上如果os.Stat如果這個函數沒有給出任何意味着該文件存在的錯誤,那么你需要檢查它是什么類型的錯誤,這里使用了這兩個函數os.IsNotExistos.IsExist

這可以理解為文件拋出錯誤的Stat ,因為它不存在或者因為它存在並且存在一些問題而拋出錯誤。

這些函數所采用的參數是類型error ,盡管您可以將nil傳遞給它,但它沒有意義。

這也指出了IsExist is not same as !IsNotExist不同的IsExist is not same as !IsNotExist ,它們是兩種不同的東西。

所以現在如果你想知道go中是否存在給定文件,我希望最好的方法是:

if _, err := os.Stat(path/to/file); !os.IsNotExist(err){
   //TODO
} 

錯過了哪些其他答案,是給予該函數的路徑實際上可能是一個目錄。 以下函數確保路徑實際上是一個文件。

func fileExists(filename string) bool {
    info, err := os.Stat(filename)
    if os.IsNotExist(err) {
        return false
    }
    return !info.IsDir()
}

另一點需要指出的是:此代碼仍然可能導致競爭條件,其中另一個線程或進程刪除或創建指定的文件,而fileExists函數正在運行。

如果您擔心這一點,請在線程中使用鎖定,序列化對此函數的訪問,或者如果涉及多個應用程序,則使用進程間信號量。 如果涉及其他應用程序,在您無法控制的情況下,我猜運氣不好。

這是我對文件存在方法的看法。 它還會檢查文件是否不是目錄,如果出現錯誤,也會返回它。

// FileExists checks if a file exists (and it is not a directory).
func FileExists(filePath string) (bool, error) {
    info, err := os.Stat(filePath)
    if err == nil {
        return !info.IsDir(), nil
    }
    if errors.Is(err, os.ErrNotExist) {
        return false, nil
    }
    return false, err
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM