ไลบรารี่มาตรฐานของ Go ไม่มีฟังก์ชั่นที่ตั้งใจเพียงเพื่อตรวจสอบว่ามีไฟล์อยู่หรือไม่ (เช่น Python os.path.exists
) เป็นวิธีสำนวนที่จะทำอะไร?
ไลบรารี่มาตรฐานของ Go ไม่มีฟังก์ชั่นที่ตั้งใจเพียงเพื่อตรวจสอบว่ามีไฟล์อยู่หรือไม่ (เช่น Python os.path.exists
) เป็นวิธีสำนวนที่จะทำอะไร?
คำตอบ:
ในการตรวจสอบว่าไม่มีไฟล์อยู่เทียบเท่ากับ Python if not os.path.exists(filename)
:
if _, err := os.Stat("/path/to/whatever"); os.IsNotExist(err) {
// path/to/whatever does not exist
}
วิธีตรวจสอบว่ามีไฟล์อยู่หรือไม่เทียบเท่ากับ Python if os.path.exists(filename)
:
แก้ไข: ต่อความคิดเห็นล่าสุด
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
เช่นถ้า/etc/bashrc
มีอยู่/etc/bashrc/foobar
จะกลับมาENOTDIR
!os.IsNotExist(err)
สภาพที่ควรจะเป็น อาจเป็นไปได้ว่าไฟล์มีอยู่ แต่os.Stat
ล้มเหลวเนื่องจากสาเหตุอื่น ๆ (เช่นสิทธิ์, ความล้มเหลวของดิสก์) การใช้err == nil
เป็นเงื่อนไขจัดหมวดหมู่ความล้มเหลวเช่น "ไฟล์ไม่มีอยู่" อย่างไม่ถูกต้อง
ตอบโดยCaleb Spare ที่โพสต์ในรายชื่อผู้รับจดหมายgonuts
[... ] มันไม่จำเป็นบ่อยนักและ [... ] การใช้
os.Stat
นั้นง่ายพอสำหรับกรณีที่จำเป็น[... ] ตัวอย่างเช่น: ถ้าคุณกำลังจะเปิดไฟล์ไม่มีเหตุผลที่จะตรวจสอบว่ามันมีอยู่ก่อน ไฟล์อาจหายไประหว่างการตรวจสอบและเปิดและคุณจะต้องตรวจสอบ
os.Open
ข้อผิดพลาดโดยไม่คำนึงถึง ดังนั้นคุณเพียงแค่โทรos.IsNotExist(err)
หลังจากที่คุณพยายามเปิดไฟล์และจัดการกับไฟล์ที่ไม่มีอยู่ที่นั่น (หากต้องใช้การจัดการพิเศษ)[... ] คุณไม่จำเป็นต้องตรวจสอบเส้นทางที่มีอยู่เลย (และคุณไม่ควรทำ)
os.MkdirAll
ทำงานได้หรือไม่เส้นทางที่มีอยู่แล้ว (คุณต้องตรวจสอบข้อผิดพลาดจากการโทรนั้นด้วย)แทนการใช้คุณควรใช้
os.Create
os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
ด้วยวิธีนี้คุณจะได้รับข้อผิดพลาดหากไฟล์นั้นมีอยู่แล้ว นอกจากนี้ยังไม่มีเงื่อนไขการแย่งชิงกับสิ่งอื่นที่ทำให้ไฟล์แตกต่างจากรุ่นของคุณที่ตรวจสอบการมีอยู่ก่อน
นำมาจาก: https://groups.google.com/forum/#!msg/golang-nuts/Ayx-BMNdMFo/4rL8FFHr8v4J
คุณควรใช้os.Stat()
และos.IsNotExist()
ฟังก์ชั่นดังในตัวอย่างต่อไปนี้:
// 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
}
เช่นโดย user11617ไม่ถูกต้อง; มันจะรายงานว่าไฟล์นั้นมีอยู่แม้ในกรณีที่ไม่มี แต่มีข้อผิดพลาดของการเรียงลำดับอื่น
ลายเซ็นควรมีอยู่ (สตริง) (บูล, ข้อผิดพลาด) และเมื่อมันเกิดขึ้นไซต์การโทรก็ไม่ได้ดีขึ้น
รหัสที่เขาเขียนจะดีกว่าเป็น:
func Exists(name string) bool {
_, err := os.Stat(name)
return !os.IsNotExist(err)
}
แต่ฉันแนะนำสิ่งนี้แทน:
func Exists(name string) (bool, error) {
_, err := os.Stat(name)
if os.IsNotExist(err) {
return false, nil
}
return err != nil, err
}
err != nil
แทนerr == nil
? หากมีข้อผิดพลาดอาจเป็นเพราะไฟล์นั้นไม่มีอยู่จริง?
คำตอบอื่น ๆ ที่พลาดคือเส้นทางที่กำหนดให้กับฟังก์ชันอาจเป็นไดเรกทอรีได้ ฟังก์ชั่นต่อไปทำให้แน่ใจว่าเส้นทางเป็นไฟล์จริงๆ
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
สิ่งอื่นที่ชี้ให้เห็น: รหัสนี้ยังสามารถนำไปสู่สภาวะการแย่งชิงซึ่งเธรดอื่นหรือกระบวนการลบหรือสร้างไฟล์ที่ระบุในขณะที่ฟังก์ชัน fileExists ทำงานอยู่
หากคุณกังวลเกี่ยวกับเรื่องนี้ให้ใช้การล็อคในกระทู้ของคุณเป็นอันดับการเข้าถึงฟังก์ชั่นนี้หรือใช้สัญญาณระหว่างกระบวนการหากมีหลายแอพพลิเคชั่นที่เกี่ยวข้อง หากแอพพลิเคชั่นอื่น ๆ ที่เกี่ยวข้องนอกเหนือจากการควบคุมของคุณคุณก็โชคไม่ดีฉันเดาว่า
_, 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)
}
ตัวอย่างฟังก์ชั่น:
func file_is_exists(f string) bool {
_, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return err == nil
}
ลองดูที่ด้านน้อยก่อนทั้งฟังก์ชั่นที่จัดทำโดยos
แพคเกจของgolang
ไม่ใช่สาธารณูปโภค แต่ตัวตรวจสอบข้อผิดพลาดสิ่งที่ฉันหมายถึงโดยที่พวกเขาเป็นเพียงเสื้อคลุมในการจัดการข้อผิดพลาดบนแพลตฟอร์มข้าม
ดังนั้นโดยทั่วไปถ้าos.Stat
หากฟังก์ชั่นนี้ไม่ได้ให้ข้อผิดพลาดใด ๆ ที่หมายถึงไฟล์จะมีอยู่ถ้ามันไม่คุณต้องตรวจสอบสิ่งที่ชนิดของข้อผิดพลาดที่มันเป็นที่นี่มาใช้งานของทั้งสองฟังก์ชั่นและos.IsNotExist
os.IsExist
ซึ่งสามารถเข้าใจได้ว่าเป็นStat
ข้อผิดพลาดในการขว้างปาไฟล์เนื่องจากไม่มีอยู่หรือเกิดข้อผิดพลาดในการขว้างปาเนื่องจากมีอยู่และมีปัญหาบางอย่าง
พารามิเตอร์ที่ฟังก์ชั่นเหล่านี้ใช้นั้นเป็นประเภทerror
ถึงแม้ว่าคุณจะสามารถผ่านnil
ไปได้ แต่ก็ไม่สมเหตุสมผล
นี่ยังชี้ให้เห็นถึงความจริงที่ว่าIsExist is not same as !IsNotExist
พวกเขาเป็นสองสิ่งที่แตกต่างกัน
ดังนั้นตอนนี้หากคุณต้องการทราบว่ามีไฟล์ที่กำหนดอยู่ในระหว่างการเดินทางฉันต้องการวิธีที่ดีที่สุดคือ:
if _, err := os.Stat(path/to/file); !os.IsNotExist(err){
//TODO
}
ตามที่ระบุไว้ในคำตอบอื่น ๆ ก็เป็นไปได้ที่จะสร้างพฤติกรรมที่จำเป็น / os.OpenFile
ข้อผิดพลาดจากการใช้ธงแตกต่างกันด้วย ในความเป็นจริงos.Create
เป็นเพียงชวเลขที่สมเหตุสมผลสำหรับการทำเช่นนั้น:
// 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)
}
คุณควรรวมการตั้งค่าสถานะเหล่านี้ด้วยตนเองเพื่อให้ได้พฤติกรรมที่คุณสนใจ:
// 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.
)
คุณจะได้รับข้อผิดพลาดต่าง ๆ ทั้งนี้ขึ้นอยู่กับสิ่งที่คุณเลือก
นี่คือตัวอย่างที่ฉันต้องการเปิดไฟล์เพื่อเขียน แต่ฉันจะตัดทอนไฟล์ที่มีอยู่เฉพาะเมื่อผู้ใช้บอกว่าตกลง
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)
}
}
วิธีที่ดีที่สุดในการตรวจสอบว่ามีไฟล์อยู่หรือไม่:
if _, err := os.Stat("/path/to/file"); err == nil || os.IsExist(err) {
// your code here if file exists
}