Bagaimana cara memeriksa apakah ada file di Go?

436

Pustaka standar Go tidak memiliki fungsi yang hanya dimaksudkan untuk memeriksa apakah ada file atau tidak (seperti Python os.path.exists). Apa cara idiomatis untuk melakukannya?

Sridhar Ratnakumar
sumber
Saya tidak begitu mengerti. Pada saat yang sama Anda mengatakan tidak ada fungsi standar dan Anda menulis jawaban dengan fungsi standar. Apa yang saya lewatkan? Bukankah paling tidak pertanyaan harus diperbaiki?
Denys Séguret
@dystroy - memperbaiki pertanyaan.
Sridhar Ratnakumar
11
Orang sebaiknya menghindari menanyakan keberadaan file. B / c dari sifat bersemangat jawaban, informasi yang diperoleh mengatakan sebenarnya tidak ada yang berguna di atas file yang ada pada saat ditanya - tetapi mungkin tidak ada lagi. Cara yang disarankan adalah dengan hanya membuka file dan memeriksa apakah itu gagal atau tidak.
zzzz
2
Ini sudah dijawab di sini
Sergey Koulikov
2
@zzzz (Saya tahu ini sudah bertahun-tahun, komentar ini untuk pembaca baru) Saya setuju dalam kasus umum. Tetapi aplikasi saya memuat pustaka pihak ketiga yang mengambil beberapa jalur file sebagai data inisialisasi tetapi segfault jika file tidak ada. Saya pikir ini adalah skenario yang valid untuk memeriksa apakah file tersebut ada dengan Anda mencoba membukanya untuk dapat melaporkan kesalahan tanpa kerusakan fatal, karena kode saya tidak perlu membaca konten file atau menulis ke file secara langsung.
Sergio Acosta

Jawaban:

693

Untuk memeriksa apakah file tidak ada, sama dengan Python if not os.path.exists(filename):

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

Untuk memeriksa apakah ada file, setara dengan Python if os.path.exists(filename):

Diedit: per komentar terbaru

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


}
Sridhar Ratnakumar
sumber
3
kadang-kadang mengembalikan ENOTDIR alih-alih NOTEXIST, misalnya, jika /etc/bashrcada, /etc/bashrc/foobarakan kembaliENOTDIR
lidaobing
43
Cuplikan kedua lebih salah; kondisi seharusnya !os.IsNotExist(err). Mungkin saja file ada tetapi os.Statgagal karena alasan lain (mis. Izin, kegagalan disk). Menggunakan err == nilsebagai kondisi salah mengkategorikan kegagalan seperti "file tidak ada".
sqweek
9
Untuk memeriksa apakah ada file yang salah: err nihil jika file ada
tangxinfa
1
Pastikan untuk memperluas ~ atau yang lain akan mengembalikan false ... stackoverflow.com/questions/17609732/…
Marcello de Sales
Anda bisa menggunakan os.IsExist () tergantung kasusnya, bisa lebih idiomatis daripada membuat negasi ganda ketika melakukan! Os.IsNotExistant ()
Ariel Monaco
126

Jawaban oleh Caleb Spare diposting di milis gonuts .

[...] Ini sebenarnya tidak terlalu sering dibutuhkan dan [...] os.Statcukup mudah digunakan untuk kasus-kasus di mana diperlukan.

[...] Misalnya: jika Anda akan membuka file, tidak ada alasan untuk memeriksa apakah file itu ada terlebih dahulu. File bisa menghilang di antara memeriksa dan membuka, dan lagi pula Anda harus memeriksa os.Openkesalahannya. Jadi, Anda cukup menelepon os.IsNotExist(err)setelah Anda mencoba membuka file, dan berurusan dengan tidak adanya di sana (jika itu memerlukan penanganan khusus).

[...] Anda tidak perlu memeriksa jalur yang ada sama sekali (dan Anda seharusnya tidak).

  • os.MkdirAllberfungsi apakah jalan sudah ada atau tidak. (Anda juga perlu memeriksa kesalahan dari panggilan itu.)

  • Alih-alih menggunakan os.Create, Anda harus menggunakan os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666). Dengan begitu Anda akan mendapatkan kesalahan jika file tersebut sudah ada. Juga ini tidak memiliki kondisi balapan dengan sesuatu yang lain membuat file, tidak seperti versi Anda yang memeriksa keberadaan sebelumnya.

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

OscarRyz
sumber
31

Anda harus menggunakan os.Stat()dan os.IsNotExist()fungsinya seperti pada contoh berikut:

// 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
}

Contohnya diambil dari sini .

Afriza N. Arief
sumber
12
Hati-hati: seperti yang ditunjukkan oleh stackoverflow.com/a/22467409/712014 , kode ini mengembalikan true, bahkan jika file tersebut tidak ada, misalnya ketika Stat () mengembalikan izin ditolak.
Michael
19

The misalnya dengan user11617 tidak benar; itu akan melaporkan bahwa file itu ada bahkan dalam kasus di mana tidak, tetapi ada kesalahan dari beberapa jenis lainnya.

Tanda tangan harus ada (string) (bool, error). Dan kemudian, seperti yang terjadi, situs panggilan tidak lebih baik.

Kode yang ditulisnya akan lebih baik sebagai:

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

Tapi saya menyarankan ini sebagai gantinya:

func Exists(name string) (bool, error) {
  _, err := os.Stat(name)
  if os.IsNotExist(err) {
    return false, nil
  }
  return err != nil, err
}
pengguna3431012
sumber
7
Apa itu contoh 5? Bisakah Anda lebih spesifik?
xlm
1
Contoh kedua Anda perlu merusak beberapa nilai kembali - misalnya _, err: = os.Stat (nama)
David Duncan
6
Mengapa kembali err != nilbukan err == nil? Jika ada kesalahan, maka file tersebut mungkin tidak ada?
idbrii
14

Apa jawaban lain yang terlewatkan, adalah bahwa jalur yang diberikan ke fungsi sebenarnya bisa berupa direktori. Fungsi berikut memastikan, bahwa path benar-benar file.

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

Hal lain yang perlu diperhatikan: Kode ini masih bisa mengarah pada kondisi balapan, di mana utas atau proses lain menghapus atau membuat file yang ditentukan, sementara fungsi fileExists sedang berjalan.

Jika Anda khawatir tentang ini, gunakan kunci di utas Anda, buat serialkan akses ke fungsi ini atau gunakan semafor antar-proses jika beberapa aplikasi terlibat. Jika aplikasi lain terlibat, di luar kendali Anda, Anda kurang beruntung, saya kira.

ZuBsPaCe
sumber
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)
    }
tangxinfa
sumber
7

Contoh fungsi:

func file_is_exists(f string) bool {
    _, err := os.Stat(f)
    if os.IsNotExist(err) {
        return false
    }
    return err == nil
}
honmaple
sumber
1
Bukankah itu berlebihan?
Ilia Choly
6

Mari kita lihat beberapa aspek terlebih dahulu, kedua fungsi yang disediakan oleh ospaket golangbukan utilitas tetapi pemeriksa kesalahan, yang saya maksud dengan itu adalah mereka hanya pembungkus untuk menangani kesalahan pada lintas platform.

Jadi pada dasarnya jika os.Statjika fungsi ini tidak memberikan kesalahan yang berarti file itu ada jika Anda perlu memeriksa jenis kesalahan itu, inilah penggunaan kedua fungsi ini os.IsNotExistdan os.IsExist.

Ini dapat dipahami sebagai Statkesalahan melempar file karena tidak ada atau melempar kesalahan karena ada dan ada beberapa masalah dengan itu.

Parameter yang digunakan fungsi-fungsi ini adalah tipe error, meskipun Anda mungkin bisa meneruskannya niltetapi tidak masuk akal.

Ini juga menunjukkan fakta bahwa IsExist is not same as !IsNotExist, mereka dua cara yang berbeda.

Jadi sekarang jika Anda ingin tahu apakah file yang diberikan ada di mana saja, saya lebih suka cara terbaik adalah:

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

Seperti disebutkan dalam jawaban lain, adalah mungkin untuk membangun perilaku / kesalahan yang diperlukan dari menggunakan flag yang berbeda dengan os.OpenFile. Faktanya, os.Createhanya singkatan yang masuk akal-default untuk melakukannya:

// 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)
}

Anda harus menggabungkan bendera ini sendiri untuk mendapatkan perilaku yang Anda minati:

// 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.
)

Tergantung pada apa yang Anda pilih, Anda akan mendapatkan kesalahan yang berbeda.

Berikut adalah contoh di mana saya ingin membuka file untuk ditulis, tetapi saya hanya akan memotong file yang sudah ada jika pengguna mengatakan tidak apa-apa:

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) 
   }
}
Sebastian N
sumber
0

Cara terbaik untuk memeriksa apakah file ada:

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