Standardowa biblioteka Go nie ma funkcji służącej wyłącznie do sprawdzania, czy plik istnieje, czy nie (takiej jak Python os.path.exists
). Jak idiomatyczny sposób to zrobić?
Standardowa biblioteka Go nie ma funkcji służącej wyłącznie do sprawdzania, czy plik istnieje, czy nie (takiej jak Python os.path.exists
). Jak idiomatyczny sposób to zrobić?
Odpowiedzi:
Aby sprawdzić, czy plik nie istnieje, odpowiednik Pythona if not os.path.exists(filename)
:
if _, err := os.Stat("/path/to/whatever"); os.IsNotExist(err) {
// path/to/whatever does not exist
}
Aby sprawdzić, czy plik istnieje, odpowiednik Pythona if os.path.exists(filename)
:
Edytowane: według ostatnich komentarzy
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
, na przykład, jeśli /etc/bashrc
istnieje, /etc/bashrc/foobar
zwróciENOTDIR
!os.IsNotExist(err)
. Możliwe, że plik istnieje, ale os.Stat
nie działa z innych powodów (np. Pozwolenie, awaria dysku). Użycie err == nil
jako warunku niepoprawnie klasyfikuje takie awarie jako „plik nie istnieje”.
Odpowiedz przez Caleb Spare opublikowany na liście mailingowej gonuts .
[...] W rzeczywistości nie jest to bardzo często potrzebne, a [...] korzystanie
os.Stat
jest dość łatwe w przypadkach, w których jest to wymagane.[...] Na przykład: jeśli zamierzasz otworzyć plik, nie ma powodu, aby sprawdzać, czy plik istnieje najpierw. Plik może zniknąć między sprawdzaniem a otwieraniem, a mimo to musisz sprawdzić
os.Open
błąd niezależnie od tego. Więc po prostu zadzwońos.IsNotExist(err)
po próbie otwarcia pliku i poradzić sobie z jego nieistnieniem (jeśli wymaga to specjalnej obsługi).[...] Nie musisz wcale sprawdzać istniejących ścieżek (i nie powinieneś).
os.MkdirAll
działa niezależnie od tego, czy ścieżki już istnieją. (Musisz także sprawdzić błąd z tego połączenia).Zamiast używać
os.Create
, powinieneś użyćos.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
. W ten sposób pojawi się błąd, jeśli plik już istnieje. W przeciwieństwie do twojej wersji, która wcześniej sprawdza istnienie, nie jest to warunek wyścigu z czymś innym, co tworzy plik.
Zaczerpnięte z: https://groups.google.com/forum/#!msg/golang-nuts/Ayx-BMNdMFo/4rL8FFHr8v4J
Należy użyć funkcji os.Stat()
i, os.IsNotExist()
jak w poniższym przykładzie:
// 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
}
Przykład został wyciągnięty stąd .
Przykład przez user11617 jest nieprawidłowe; zgłosi, że plik istnieje nawet w przypadkach, w których go nie ma, ale wystąpił błąd innego rodzaju.
Podpis powinien mieć postać Exists (string) (bool, error). A potem, jak to się dzieje, strony połączeń nie są lepsze.
Kod, który napisał, byłby lepszy jako:
func Exists(name string) bool {
_, err := os.Stat(name)
return !os.IsNotExist(err)
}
Ale proponuję to zamiast tego:
func Exists(name string) (bool, error) {
_, err := os.Stat(name)
if os.IsNotExist(err) {
return false, nil
}
return err != nil, err
}
err != nil
zamiast err == nil
? Jeśli wystąpił błąd, plik prawdopodobnie nie istnieje?
Inne pominięte odpowiedzi to to, że ścieżka podana do funkcji może być katalogiem. Poniższa funkcja zapewnia, że ścieżka jest tak naprawdę plikiem.
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
Kolejna rzecz, na którą należy zwrócić uwagę: ten kod może nadal prowadzić do wyścigu, w którym inny wątek lub proces usuwa lub tworzy określony plik, gdy funkcja fileExists jest uruchomiona.
Jeśli martwisz się tym, użyj blokady w wątkach, szereguj dostęp do tej funkcji lub skorzystaj z semafora między procesami, jeśli zaangażowanych jest wiele aplikacji. Jeśli w grę wchodzą inne aplikacje, poza twoją kontrolą, nie masz szczęścia.
Przykład funkcji:
func file_is_exists(f string) bool {
_, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return err == nil
}
Spójrzmy najpierw na kilka aspektów, obie funkcje dostarczane przez os
pakiet golang
nie są narzędziami, ale narzędziami do sprawdzania błędów, co mam na myśli to, że są one tylko opakowaniem do obsługi błędów na różnych platformach.
Zasadniczo, jeśli os.Stat
jeśli ta funkcja nie daje żadnego błędu, oznacza to, że plik istnieje, jeśli musisz sprawdzić, jaki to jest błąd, oto użycie tych dwóch funkcji os.IsNotExist
i os.IsExist
.
Można to rozumieć jako Stat
błąd zgłaszania pliku, ponieważ nie istnieje lub jest to błąd zgłaszania, ponieważ istnieje i jest z nim jakiś problem.
Parametr, który te funkcje przyjmują, jest typem error
, chociaż możesz być w stanie przekazaćnil
, ale nie ma to sensu.
Wskazuje to również na fakt IsExist is not same as !IsNotExist
, że są to dwie różne rzeczy.
Więc jeśli chcesz wiedzieć, czy dany plik istnieje w go, wolałbym najlepiej:
if _, err := os.Stat(path/to/file); !os.IsNotExist(err){
//TODO
}
Jak wspomniano w innych odpowiedziach, możliwe jest skonstruowanie wymaganego zachowania / błędów przy użyciu różnych flag z os.OpenFile
. W rzeczywistości os.Create
jest to po prostu rozsądny skrót do robienia tego:
// 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)
}
Powinieneś sam połączyć te flagi, aby uzyskać interesujące Cię zachowanie:
// 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.
)
W zależności od tego, co wybierzesz, będziesz otrzymywać różne błędy.
Oto przykład, w którym chcę otworzyć plik do zapisu, ale obetnę istniejący plik tylko wtedy, gdy użytkownik powie, że jest 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)
}
}