Làm thế nào để kiểm tra nếu một tập tin tồn tại trong Go?


435

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 đó?


Tôi không thực sự hiểu nó. Đồng thời bạn nói không có chức năng tiêu chuẩn và bạn viết câu trả lời với chức năng tiêu chuẩn. Tôi đang thiếu gì? Không nên ít nhất là câu hỏi được sửa chữa?
Denys Séguret

@dystroy - sửa câu hỏi.
Sridhar Ratnakumar

11
Tốt hơn nên tránh sự tồn tại của tập tin. B / c về bản chất không phù hợp của câu trả lời, thông tin thu được cho biết thực sự không có gì hữu ích ở trên tệp tồn tại trong thời gian được hỏi - nhưng nó có thể không tồn tại nữa. Cách được khuyến nghị là chỉ cần mở một tệp và kiểm tra xem điều đó có thất bại hay không.
zzzz

2
Điều này đã được trả lời ở đây
Sergey Koulikov

2
@zzzz (Tôi biết đã nhiều năm rồi, bình luận này dành cho những độc giả mới) Tôi đồng ý trong trường hợp chung. Nhưng ứng dụng của tôi tải thư viện bên thứ ba lấy một số đường dẫn tệp làm dữ liệu khởi tạo nhưng tách biệt nếu tệp không tồn tại. Tôi nghĩ rằng đây là một kịch bản hợp lệ để kiểm tra xem tệp có tồn tại hay không khi cố mở nó để có thể báo cáo lỗi mà không gặp sự cố nghiêm trọng, vì mã của tôi không cần phải đọc nội dung tệp hoặc ghi trực tiếp vào tệp.
Sergio Acosta

Câu trả lời:


690

Để 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


}

3
đôi khi, nó trả lại ENOTDIR thay vì NOTEXIST, ví dụ, nếu /etc/bashrctồn tại, /etc/bashrc/foobarnó sẽ quay trở lạiENOTDIR
nắp vào

43
Đoạn mã thứ hai là sai tinh tế hơn; điều kiện nên được !os.IsNotExist(err). Có thể tệp tồn tại nhưng os.Statkhông thành công vì các lý do khác (ví dụ: quyền, lỗi đĩa). Sử dụng err == nilnhư đ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".
sqweek

9
Để kiểm tra xem một tệp tồn tại có sai hay không: err is nil if file tồn tại
tangxinfa

1
Đảm bảo mở rộng ~ nếu không nó sẽ trả về false ... stackoverflow.com/questions/17609732/ Khăn
Marcello de Sales

Bạn có thể sử dụng os.IsExist () tùy theo trường hợp, có thể có ý nghĩa hơn thay vì thực hiện phủ định kép khi thực hiện! Os.IsNotExistant ()
Ariel Monaco

126

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.Statlà đủ 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.Openlỗi bất kể. Vì vậy, bạn chỉ cần gọi os.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.MkdirAllhoạ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ụng os.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


30

Bạn nên sử dụng các hàm os.Stat()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 .


12
Coi chừng: như stackoverflow.com/a/22467409/712014 đã chỉ ra, mã này trả về đúng, ngay cả khi tệp không tồn tại, ví dụ khi Stat () trả về quyền bị từ chối.
Michael

19

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
}

7
Ví dụ 5 là gì? Bạn có thể được cụ thể xin vui lòng.
xlm 17/03/2016

1
Ví dụ thứ hai của bạn cần cấu trúc nhiều giá trị trả về - ví dụ: _, err: = os.Stat (tên)
David Duncan

6
Tại sao trở về err != nilthay vì err == nil? Nếu có lỗi, thì tập tin có lẽ không tồn tại?
idbrii

14

Đ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.


12
    _, 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)
    }

7

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
}

1
Không phải là nếu dư thừa?
Ilia Choly

6

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 osgói golangkhô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.Stathà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.IsNotExistos.IsExist.

Điều này có thể được hiểu là Statlỗ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 nilsang 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
} 

1

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.Createchỉ 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) 
   }
}

0

Cách tốt nhất để kiểm tra nếu tập tin tồn tại:

if _, err := os.Stat("/path/to/file"); err == nil || os.IsExist(err) {
    // your code here if file exists
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.