golang如何判断文件是否存在

 更新时间:2024年11月26日 11:35:20   作者:apocelipes  
判断一个文件是否存在是一个相当常见的需求,在golang中也有多种方案实现这一功能,下面就跟随小编一起学习一下具体的实现方法吧

判断一个文件是否存在是一个相当常见的需求,在golang中也有多种方案实现这一功能。

现在我们介绍其中两种最常用也是最简单的实现,第一种将是跨平台通用的,而第二种则在POSIX平台上通用。

跨平台实现

跨平台实现的思路很简单,如果某个文件不存在,那么使用os.Lstat就一定会返回error,只要判断error是否代表文件不存在即可。

也许你注意到了有些代码会使用os.Open来完成上述工作,不过最好不要这么做,因为虽然两者完成的功能没有区别,但open和stat的调用开销是不同的,后者要小于前者,而且对于判断文件是否存在,检查它的元数据要比直接尝试打开它更加合理。

那么来看看实现的代码:

func FileExist(path string) bool {
  _, err := os.Lstat(path)
  return !os.IsNotExist(err)
}

代码很简单,对于Windows/Linux/MacOS等是通用的,一般没有特殊需求我也比较推荐这种实现。

POSIX平台实现

如果你的程序是面向POSIX平台的(例如UNIX、Linux等),那么还有一种更简单的方案——syscall.Access

syscall.Access提供了用户检查文件元信息的手段,通常它被用来检查文件权限以及文件的存在性。

通过使用syscall.F_OK标志检查文件,如果不存在则会返回和os.Lstat一样的error:

func FileExist(path string) bool {
  err := syscall.Access(path, syscall.F_OK)
  return !os.IsNotExist(err)
}

这种实现的最大优势在于它简单而直观,但是它无法在Windows上使用。

一些提示

首先当我们的FileExist返回true时,其实文件并不一定存在。

当我们对目标path中的某一部分没有可读权限时,os.Lstatsyscall.Access同样会返回error,不过这个error不会让os.IsNotExist返回true。

当文件不存在而你对文件所在的目录或者它的上层目录没有访问权限时,FileExist依旧会返回true,bug就在这时发生了。所以重要的一点是在判断文件是否存在前应该先判断自己对文件及其路径是否有访问权限

其次syscall.Access只会使用运行程序的用户的uid和gid,这会导致setuid之类的权限失效,通常来说这是没什么问题的,然而posix平台上一般都会考虑euid和egid,因此你可能需要使用syscall.Faccessat做代替。你需要在深思熟虑后使用合适的系统调用。

性能测试

最后我们看看两个方案的性能,我们以os.Open做为基准,分别测试先文件存在和不存在时的性能表现:

func checkWithOpen(path string) bool {
	f, err := os.Open(path)
	if err != nil {
		return false
	}
	f.Close()
	return true
}

func checkWithLstat(path string) bool {
	_, err := os.Lstat(path)
	return !os.IsNotExist(err)
}

func checkWithAccess(path string) bool {
        err := syscall.Access(path, syscall.F_OK)
	return !os.IsNotExist(err)
}

func BenchmarkNotExists(b *testing.B) {
	for range b.N {
		checkWithOpen("/home/apocelipes/no-")
	}
}

func BenchmarkNotExistsLstat(b *testing.B) {
	for range b.N {
		checkWithLstat("/home/apocelipes/no-")
	}
}

func BenchmarkNotExistsAccess(b *testing.B) {
	for range b.N {
		checkWithAccess("/home/apocelipes/no-")
	}
}

func BenchmarkExists(b *testing.B) {
	for range b.N {
		checkWithOpen("/home/apocelipes/.zshrc")
	}
}

func BenchmarkExistsLstat(b *testing.B) {
	for range b.N {
		checkWithLstat("/home/apocelipes/.zshrc")
	}
}

func BenchmarkExistsAccess(b *testing.B) {
	for range b.N {
		checkWithAccess("/home/apocelipes/.zshrc")
	}
}

这是结果:

测试使用的文件系统类型是XFS。

可以看到open是最慢的,lstat比access慢了16%左右。从结果里也可以看到lstat需要额外返回一个os.FileInfo结构导致了额外的内存分配,所以整体上速度更慢。

但考虑到跨平台以及兼容性,使用os.Lstat是更常见的做法。

以上就是golang如何判断文件是否存在的详细内容,更多关于go判断文件是否存在的资料请关注脚本之家其它相关文章!

相关文章

  • 详解Go语言如何解析带注释的json

    详解Go语言如何解析带注释的json

    标准的json格式是不带注释,但是有时候为了方便理解json中各字段的含义,需要支持带注释的json,这篇文章主要介绍了Go语言解析带注释json的相关方法,希望对大家有所帮助
    2024-03-03
  • Go语言strconv包实现字符串和数值类型的相互转换

    Go语言strconv包实现字符串和数值类型的相互转换

    这篇文章主要介绍了Go语言strconv包实现字符串和数值类型的相互转换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Go语言使用goroutine及通道实现并发详解

    Go语言使用goroutine及通道实现并发详解

    这篇文章主要为大家介绍了Go语言使用goroutine及通道实现并发详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Golang Gorm 更新字段save、update、updates

    Golang Gorm 更新字段save、update、updates

    在gorm中,批量更新操作可以通过使用Update方法来实现,本文主要介绍了Golang Gorm 更新字段save、update、updates,具有一定的参考价值,感兴趣的可以了解一下
    2023-12-12
  • Go部署多环境开发的项目实战

    Go部署多环境开发的项目实战

    在 Go 项目的开发过程中,能够在不同的环境(如开发、测试、生产)中进行灵活部署是至关重要的,不同环境通常需要不同的配置,下面就来详细的介绍一下Go部署多环境
    2026-02-02
  • 浅谈go中defer的一个隐藏功能

    浅谈go中defer的一个隐藏功能

    这篇文章主要介绍了浅谈go中defer的一个隐藏功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • golang中包无法引入问题解决

    golang中包无法引入问题解决

    本文主要介绍了golang中包无法引入问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • Go 并发实现协程同步的多种解决方法

    Go 并发实现协程同步的多种解决方法

    这篇文章主要介绍了Go 并发——实现协程同步的多种解决方法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-08-08
  • 如何使用Go语言实现远程执行命令

    如何使用Go语言实现远程执行命令

    远程执行命令最常用的方法就是利用SSH协议,将命令发送到远程机器上执行,并获取返回结果。本文将介绍如何使用Go语言实现远程执行命令。下面一起来看看。
    2016-08-08
  • 详解Go hash算法的支持

    详解Go hash算法的支持

    这篇文章主要介绍了详解Go hash算法的支持,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09

最新评论