จะตรวจสอบว่าไฟล์นั้นมีอยู่ใน Go ได้อย่างไร?


436

ไลบรารี่มาตรฐานของ Go ไม่มีฟังก์ชั่นที่ตั้งใจเพียงเพื่อตรวจสอบว่ามีไฟล์อยู่หรือไม่ (เช่น Python os.path.exists) เป็นวิธีสำนวนที่จะทำอะไร?


ฉันไม่เข้าใจจริงๆ ในนาทีเดียวกันคุณบอกว่าไม่มีฟังก์ชั่นมาตรฐานและคุณเขียนคำตอบด้วยฟังก์ชั่นมาตรฐาน ฉันพลาดอะไรไป อย่างน้อยไม่ควรมีการแก้ไขคำถาม?
Denys Séguret

@dystroy - แก้ไขคำถาม
Sridhar Ratnakumar

11
ควรหลีกเลี่ยงการสอบถามไฟล์ที่มีอยู่ B / c ของธรรมชาติที่มีชีวิตชีวาของคำตอบข้อมูลที่ได้รับบอกว่าจริง ๆ แล้วไม่มีอะไรมีประโยชน์เหนือไฟล์ที่มีอยู่ในเวลาที่ถาม - แต่มันอาจจะไม่อยู่อีกต่อไป วิธีที่แนะนำคือเพียงเปิดไฟล์และตรวจสอบว่ามันล้มเหลวหรือไม่
zzzz

2
สิ่งนี้ได้รับคำตอบแล้วที่นี่
Sergey Koulikov

2
@zzzz (ฉันรู้ว่ามันเป็นปีแล้วความคิดเห็นนี้มีไว้สำหรับผู้อ่านใหม่) ฉันเห็นด้วยในกรณีทั่วไป แต่แอพของฉันโหลดไลบรารี่ของบุคคลที่สามซึ่งใช้พา ธ ของไฟล์เป็นข้อมูลเริ่มต้น แต่ segfaults หากไฟล์นั้นไม่มีอยู่ ฉันคิดว่านี่เป็นสถานการณ์ที่ถูกต้องสำหรับการตรวจสอบว่ามีไฟล์อยู่หรือไม่โดยพยายามเปิดไฟล์เพื่อให้สามารถรายงานข้อผิดพลาดได้โดยไม่มีข้อผิดพลาดร้ายแรงเนื่องจากรหัสของฉันไม่จำเป็นต้องอ่านเนื้อหาของไฟล์หรือเขียนลงไฟล์โดยตรง
Sergio Acosta

คำตอบ:


693

ในการตรวจสอบว่าไม่มีไฟล์อยู่เทียบเท่ากับ 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


}

3
บางครั้งมันกลับ ENOTDIR แทนNOTEXISTเช่นถ้า/etc/bashrcมีอยู่/etc/bashrc/foobarจะกลับมาENOTDIR
lidaobing

43
ตัวอย่างที่สองผิดไปอย่างละเอียดยิ่งขึ้น !os.IsNotExist(err)สภาพที่ควรจะเป็น อาจเป็นไปได้ว่าไฟล์มีอยู่ แต่os.Statล้มเหลวเนื่องจากสาเหตุอื่น ๆ (เช่นสิทธิ์, ความล้มเหลวของดิสก์) การใช้err == nilเป็นเงื่อนไขจัดหมวดหมู่ความล้มเหลวเช่น "ไฟล์ไม่มีอยู่" อย่างไม่ถูกต้อง
sqweek

9
ในการตรวจสอบว่ามีไฟล์ผิดหรือไม่: err เป็นศูนย์หากไฟล์นั้นมีอยู่จริง
tangxinfa

1
อย่าลืมขยาย ~ มิฉะนั้นจะส่งคืน false ... stackoverflow.com/questions/17609732//
Marcello de Sales

คุณสามารถใช้ os.IsExist () แล้วแต่กรณีอาจเป็นเรื่องแปลกมากกว่าที่จะทำสิ่งที่ปฏิเสธเมื่อทำ! os.IsNotExistant ()
Ariel Monaco

126

ตอบโดย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


31

คุณควรใช้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
}

ตัวอย่างที่สกัดจากที่นี่


12
ระวัง: เนื่องจากstackoverflow.com/a/22467409/712014ชี้ให้เห็นว่ารหัสนี้ส่งคืนจริงแม้ว่าไฟล์จะไม่มีอยู่ตัวอย่างเช่นเมื่อ Stat () ส่งคืนสิทธิ์ที่ถูกปฏิเสธ
Michael

19

เช่นโดย 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
}

7
ตัวอย่าง 5 คืออะไร โปรดระบุด้วย
xlm

1
ตัวอย่างที่สองของคุณจำเป็นต้องจัดโครงสร้างค่าส่งคืนหลายค่าเช่น _, err: = os.Stat (ชื่อ)
David Duncan

6
กลับมาทำไมerr != nilแทนerr == nil? หากมีข้อผิดพลาดอาจเป็นเพราะไฟล์นั้นไม่มีอยู่จริง?
idbrii

14

คำตอบอื่น ๆ ที่พลาดคือเส้นทางที่กำหนดให้กับฟังก์ชันอาจเป็นไดเรกทอรีได้ ฟังก์ชั่นต่อไปทำให้แน่ใจว่าเส้นทางเป็นไฟล์จริงๆ

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

สิ่งอื่นที่ชี้ให้เห็น: รหัสนี้ยังสามารถนำไปสู่สภาวะการแย่งชิงซึ่งเธรดอื่นหรือกระบวนการลบหรือสร้างไฟล์ที่ระบุในขณะที่ฟังก์ชัน fileExists ทำงานอยู่

หากคุณกังวลเกี่ยวกับเรื่องนี้ให้ใช้การล็อคในกระทู้ของคุณเป็นอันดับการเข้าถึงฟังก์ชั่นนี้หรือใช้สัญญาณระหว่างกระบวนการหากมีหลายแอพพลิเคชั่นที่เกี่ยวข้อง หากแอพพลิเคชั่นอื่น ๆ ที่เกี่ยวข้องนอกเหนือจากการควบคุมของคุณคุณก็โชคไม่ดีฉันเดาว่า


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

ตัวอย่างฟังก์ชั่น:

func file_is_exists(f string) bool {
    _, err := os.Stat(f)
    if os.IsNotExist(err) {
        return false
    }
    return err == nil
}

1
ไม่ซ้ำซ้อนถ้าไม่?
Ilia Choly

6

ลองดูที่ด้านน้อยก่อนทั้งฟังก์ชั่นที่จัดทำโดยosแพคเกจของgolangไม่ใช่สาธารณูปโภค แต่ตัวตรวจสอบข้อผิดพลาดสิ่งที่ฉันหมายถึงโดยที่พวกเขาเป็นเพียงเสื้อคลุมในการจัดการข้อผิดพลาดบนแพลตฟอร์มข้าม

ดังนั้นโดยทั่วไปถ้าos.Statหากฟังก์ชั่นนี้ไม่ได้ให้ข้อผิดพลาดใด ๆ ที่หมายถึงไฟล์จะมีอยู่ถ้ามันไม่คุณต้องตรวจสอบสิ่งที่ชนิดของข้อผิดพลาดที่มันเป็นที่นี่มาใช้งานของทั้งสองฟังก์ชั่นและos.IsNotExistos.IsExist

ซึ่งสามารถเข้าใจได้ว่าเป็นStatข้อผิดพลาดในการขว้างปาไฟล์เนื่องจากไม่มีอยู่หรือเกิดข้อผิดพลาดในการขว้างปาเนื่องจากมีอยู่และมีปัญหาบางอย่าง

พารามิเตอร์ที่ฟังก์ชั่นเหล่านี้ใช้นั้นเป็นประเภทerrorถึงแม้ว่าคุณจะสามารถผ่านnilไปได้ แต่ก็ไม่สมเหตุสมผล

นี่ยังชี้ให้เห็นถึงความจริงที่ว่าIsExist is not same as !IsNotExistพวกเขาเป็นสองสิ่งที่แตกต่างกัน

ดังนั้นตอนนี้หากคุณต้องการทราบว่ามีไฟล์ที่กำหนดอยู่ในระหว่างการเดินทางฉันต้องการวิธีที่ดีที่สุดคือ:

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

1

ตามที่ระบุไว้ในคำตอบอื่น ๆ ก็เป็นไปได้ที่จะสร้างพฤติกรรมที่จำเป็น / 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) 
   }
}

0

วิธีที่ดีที่สุดในการตรวจสอบว่ามีไฟล์อยู่หรือไม่:

if _, err := os.Stat("/path/to/file"); err == nil || os.IsExist(err) {
    // your code here if file exists
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.