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?
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?
Jawaban:
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
}
NOTEXIST
, misalnya, jika /etc/bashrc
ada, /etc/bashrc/foobar
akan kembaliENOTDIR
!os.IsNotExist(err)
. Mungkin saja file ada tetapi os.Stat
gagal karena alasan lain (mis. Izin, kegagalan disk). Menggunakan err == nil
sebagai kondisi salah mengkategorikan kegagalan seperti "file tidak ada".
Jawaban oleh Caleb Spare diposting di milis gonuts .
[...] Ini sebenarnya tidak terlalu sering dibutuhkan dan [...]
os.Stat
cukup 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.Open
kesalahannya. Jadi, Anda cukup meneleponos.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.MkdirAll
berfungsi apakah jalan sudah ada atau tidak. (Anda juga perlu memeriksa kesalahan dari panggilan itu.)Alih-alih menggunakan
os.Create
, Anda harus menggunakanos.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
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 .
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
}
err != nil
bukan err == nil
? Jika ada kesalahan, maka file tersebut mungkin tidak ada?
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.
Contoh fungsi:
func file_is_exists(f string) bool {
_, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return err == nil
}
Mari kita lihat beberapa aspek terlebih dahulu, kedua fungsi yang disediakan oleh os
paket golang
bukan utilitas tetapi pemeriksa kesalahan, yang saya maksud dengan itu adalah mereka hanya pembungkus untuk menangani kesalahan pada lintas platform.
Jadi pada dasarnya jika os.Stat
jika fungsi ini tidak memberikan kesalahan yang berarti file itu ada jika Anda perlu memeriksa jenis kesalahan itu, inilah penggunaan kedua fungsi ini os.IsNotExist
dan os.IsExist
.
Ini dapat dipahami sebagai Stat
kesalahan 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 nil
tetapi 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
}
Seperti disebutkan dalam jawaban lain, adalah mungkin untuk membangun perilaku / kesalahan yang diperlukan dari menggunakan flag yang berbeda dengan os.OpenFile
. Faktanya, os.Create
hanya 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)
}
}