Thư viện chuẩn của Go không có chức năng chỉ nhằm mục đích kiểm tra xem một tệp có tồn tại hay không (như của Python os.path.exists
). Là gì ngữ cách để làm điều đó?
Thư viện chuẩn của Go không có chức năng chỉ nhằm mục đích kiểm tra xem một tệp có tồn tại hay không (như của Python os.path.exists
). Là gì ngữ cách để làm điều đó?
Câu trả lời:
Để kiểm tra xem một tệp không tồn tại, tương đương với Python if not os.path.exists(filename)
:
if _, err := os.Stat("/path/to/whatever"); os.IsNotExist(err) {
// path/to/whatever does not exist
}
Để kiểm tra xem một tệp có tồn tại không, tương đương với Python if os.path.exists(filename)
:
Đã chỉnh sửa: mỗi bình luận gần đây
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
}
NOTEXIST
, ví dụ, nếu /etc/bashrc
tồn tại, /etc/bashrc/foobar
nó sẽ quay trở lạiENOTDIR
!os.IsNotExist(err)
. Có thể tệp tồn tại nhưng os.Stat
không thành công vì các lý do khác (ví dụ: quyền, lỗi đĩa). Sử dụng err == nil
như điều kiện không chính xác phân loại các lỗi như "tệp không tồn tại".
Trả lời bởi Caleb tùng được đăng trong danh sách gửi thư của gonuts .
[...] Nó thực sự không cần thiết thường xuyên và [...] sử dụng
os.Stat
là đủ dễ dàng cho các trường hợp cần thiết.[...] Ví dụ: nếu bạn định mở tệp, không có lý do gì để kiểm tra xem nó có tồn tại trước không. Các tập tin có thể biến mất ở giữa kiểm tra và mở, và dù sao bạn sẽ cần kiểm tra
os.Open
lỗi bất kể. Vì vậy, bạn chỉ cần gọios.IsNotExist(err)
sau khi bạn cố gắng mở tệp và xử lý sự không tồn tại của nó ở đó (nếu điều đó đòi hỏi phải xử lý đặc biệt).[...] Bạn không cần phải kiểm tra các đường dẫn hiện có (và bạn không nên).
os.MkdirAll
hoạt động cho dù các đường dẫn đã tồn tại hay chưa. (Ngoài ra, bạn cần kiểm tra lỗi từ cuộc gọi đó.)Thay vì sử dụng
os.Create
, bạn nên sử dụngos.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
. Bằng cách đó, bạn sẽ gặp lỗi nếu tệp đã tồn tại. Ngoài ra, điều này không có điều kiện chạy đua với thứ khác tạo tệp, không giống như phiên bản của bạn kiểm tra sự tồn tại trước đó.
Lấy từ: https://groups.google.com/forum/#!msg/golang-nuts/Ayx-BMNdMFo/4rL8FFHr8v4J
Bạn nên sử dụng các hàm os.Stat()
và os.IsNotExist()
như trong ví dụ sau:
// 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
}
Ví dụ được trích xuất từ đây .
Các ví dụ bằng cách user11617 là không chính xác; nó sẽ báo cáo rằng tập tin tồn tại ngay cả trong trường hợp không có, nhưng có một số lỗi khác.
Chữ ký phải là Tồn tại (chuỗi) (bool, lỗi). Và sau đó, như nó xảy ra, các trang web cuộc gọi không tốt hơn.
Mã anh viết sẽ tốt hơn là:
func Exists(name string) bool {
_, err := os.Stat(name)
return !os.IsNotExist(err)
}
Nhưng tôi đề nghị điều này thay vào đó:
func Exists(name string) (bool, error) {
_, err := os.Stat(name)
if os.IsNotExist(err) {
return false, nil
}
return err != nil, err
}
err != nil
thay vì err == nil
? Nếu có lỗi, thì tập tin có lẽ không tồn tại?
Điều mà các câu trả lời khác đã bỏ lỡ, là đường dẫn được cung cấp cho hàm thực sự có thể là một thư mục. Chức năng sau đây đảm bảo rằng đường dẫn thực sự là một tệp.
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
Một điều khác cần chỉ ra: Mã này vẫn có thể dẫn đến tình trạng chủng tộc, trong đó một luồng hoặc quá trình khác xóa hoặc tạo tệp được chỉ định, trong khi chức năng fileExists đang chạy.
Nếu bạn lo lắng về điều này, hãy sử dụng khóa trong các luồng của bạn, tuần tự hóa quyền truy cập vào chức năng này hoặc sử dụng một semaphore giữa các quá trình nếu có nhiều ứng dụng được tham gia. Nếu các ứng dụng khác có liên quan, ngoài tầm kiểm soát của bạn, bạn sẽ không gặp may, tôi đoán vậy.
Ví dụ về hàm:
func file_is_exists(f string) bool {
_, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return err == nil
}
Trước tiên, hãy xem xét một số khía cạnh, cả hai chức năng được cung cấp bởi os
gói golang
không phải là tiện ích mà là trình kiểm tra lỗi, ý tôi là chúng chỉ là một trình bao bọc để xử lý lỗi trên nền tảng chéo.
Vì vậy, về cơ bản nếu os.Stat
hàm này không đưa ra bất kỳ lỗi nào có nghĩa là tệp đang tồn tại nếu bạn cần kiểm tra xem đó là loại lỗi gì thì ở đây có sử dụng hai hàm này os.IsNotExist
và os.IsExist
.
Điều này có thể được hiểu là Stat
lỗi ném tệp vì nó không tồn tại hoặc là lỗi ném vì nó tồn tại và có một số vấn đề với nó.
Tham số mà các hàm này có là loại error
, mặc dù bạn có thể chuyển nil
sang nó nhưng nó sẽ không có ý nghĩa.
Điều này cũng chỉ ra một thực tế rằng IsExist is not same as !IsNotExist
, chúng là hai thứ khác nhau.
Vì vậy, bây giờ nếu bạn muốn biết nếu một tập tin nhất định tồn tại, tôi muốn cách tốt nhất là:
if _, err := os.Stat(path/to/file); !os.IsNotExist(err){
//TODO
}
Như đã đề cập trong các câu trả lời khác, có thể xây dựng các hành vi / lỗi cần thiết từ việc sử dụng các cờ khác nhau với os.OpenFile
. Trong thực tế, os.Create
chỉ là một tốc ký mặc định hợp lý để làm như vậy:
// 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)
}
Bạn nên tự kết hợp những lá cờ này để có được hành vi mà bạn quan tâm:
// 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.
)
Tùy thuộc vào những gì bạn chọn, bạn sẽ nhận được các lỗi khác nhau.
Đây là một ví dụ mà tôi muốn mở một tệp để viết, nhưng tôi sẽ chỉ cắt bớt một tệp hiện có nếu người dùng đã nói rằng đó là OK:
var f *os.File
if truncateWhenExists {
// O_TRUNC - truncate regular writable file when opened.
if f, err = os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
log.Fatalln("failed to force-open file, err:", err)
}
} else {
// O_EXCL - used with O_CREATE, file must not exist
if f, err = os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644); err != nil {
log.Fatalln("failed to open file, err:", err)
}
}