golang-fileExists

检查文件是否存在

demo 代码

package main

import (
"fmt"
"os"
)

func fileExists(path string) bool {
fileInfo, err := os.Stat(path)
fmt.Println(fileInfo.IsDir(), fileInfo.Mode(), fileInfo.ModTime(), fileInfo.Name(), fileInfo.Size(), fileInfo.Sys())
if err == nil {
return true
}
fmt.Println(err.Error())
if os.IsNotExist(err) {
return false
}
return true
}
func main() {
fileExists("a.txt")
}

思路

  1. os.Stat(path) 尝试拿到某一具体路径的文件描述

Stat returns a FileInfo describing the named file. If there is an error, it will be of type *PathError.

  1. 如果返回了err 用os.IsNotExist(err) 判断是否属于文件或者目录不存在的那种err.
    如果是,那就是不存在.

sNotExist returns a boolean indicating whether the error is known to report that a file or directory does not exist. It is satisfied by ErrNotExist as well as some syscall errors

粗略源码跟踪

  • fileInfo, err := os.Stat(path)

这看定义先,根据一个路径返回文件描述结构和error,实际上调用了内部的statNolog
这里先解释下FileInfo

// Stat returns a FileInfo describing the named file.
// If there is an error, it will be of type *PathError.
func Stat(name string) (FileInfo, error) {
testlog.Stat(name)
return statNolog(name)
}
// A FileInfo describes a file and is returned by Stat and Lstat.
type FileInfo interface {
Name() string // base name of the file
Size() int64 // length in bytes for regular files; system-dependent for others 这是里bytes,一个中文不是1
Mode() FileMode // file mode bits 例如:-rw-r--r--
ModTime() time.Time // modification time
IsDir() bool // abbreviation for Mode().IsDir()
Sys() interface{} // underlying data source (can return nil)
}

内部stat

// statNolog stats a file with no test logging.
func statNolog(name string) (FileInfo, error) {
var fs fileStat
err := syscall.Stat(name, &fs.sys) //用系统的stat获取fs信息赋值给&fs.sys
if err != nil {
return nil, &PathError{"stat", name, err}
}
fillFileStatFromSys(&fs, name) // 从&fs.sys里解析出FileInfo的内容
return &fs, nil
}

在debug过程中发现这个name参数其实走了3次

  1. “.”当前目录
  2. 当前目录所在的具体目录例如”/Users/vincent/go/src/vincent.com/checkFileDicExist”
  3. 文件”./a.txt”

也就说不管是这个文件不存在还是这个目录不存在都会err


  • os.IsNotExist(err)
    假如爆出err这个函数用来判断err类型

调用内部判断

// IsNotExist returns a boolean indicating whether the error is known to
// report that a file or directory does not exist. It is satisfied by
// ErrNotExist as well as some syscall errors.
func IsNotExist(err error) bool {
return isNotExist(err)
}

大意就是检查err的内容是否包含特定的string, 是不是感觉有点简陋?

func isNotExist(err error) bool {
return checkErrMessageContent(err, "does not exist", "not found",
"has been removed", "no parent")
}

帮助函数

// checkErrMessageContent checks if err message contains one of msgs.
func checkErrMessageContent(err error, msgs ...string) bool {
if err == nil {
return false
}
err = underlyingError(err) //这个根据err类型把内部的err替换出来为了得到err.Error()
for _, msg := range msgs {
// 查看err.Error里是否包含msg
if contains(err.Error(), msg) {
return true
}
}
return false
}

// contains is a local version of strings.Contains. It knows len(sep) > 1.
func contains(s, sep string) bool {
n := len(sep)
c := sep[0]
// 逐个字符段判断是否相等
for i := 0; i+n <= len(s); i++ {
if s[i] == c && s[i:i+n] == sep {
return true
}
}
return false
}