Golang生成带扩展名的唯一文件名

huangapple go评论79阅读模式
英文:

Golang Generate Unique Filename With Extension

问题

我想在Golang中生成带有扩展名的唯一文件名,类似于只接受前缀的ioutil.TempFile。

在论坛上已经多次提到过这个问题https://groups.google.com/forum/#!topic/golang-nuts/PHgye3Hm2_0,而Go的开发者似乎坚决不打算将这个功能添加到TempFile中。

那么有什么建议的方法来处理这个问题呢?我应该只是复制/粘贴TempFile的代码并添加一个后缀参数吗?

英文:

I want to generate unique filenames in Golang with extensions. Very much akin to ioutil.TempFile, which only takes prefixes.

This has been brought up many times in the forums https://groups.google.com/forum/#!topic/golang-nuts/PHgye3Hm2_0 and the Go goes seem pretty intent on not adding that functionality to TempFile.

So what's the suggested way to handle this? Should I just copy/paste the TempFile code and add in a suffix parameter?

答案1

得分: 7

更新(2020年:原始答案来自2015年)

正如Lax答案中所指出的,Go 1.11(2018年4月)已将TempFile的前缀更改为模式。

请参见CL 105675中的提交191efbc,在问题4896之后。

> 使用TempFile的用户需要能够提供后缀,特别是在使用将文件扩展名赋予语义含义的操作系统(如Windows)时。
将文件重命名以在事后包含扩展名是不够的,因为这可能导致竞争条件。

> 如果给TempFile提供的字符串中包含“*”,则随机字符串将替换“*”。

> 例如,“myname.*.bat”将导致一个随机的文件名,如“myname.123456.bat”。

> 如果不包含“*”,则保留旧行为,并将随机数字附加到末尾。

> 如果包含多个“*”,则最后一个将被替换,从而允许病态程序员创建诸如“foo*.123456.bat”但不是“foo.123456.*.bat”的文件名。


原始答案(2015年)

> 我应该只是复制/粘贴TempFile代码并添加后缀参数吗?

那是一种方法。
另一种方法是进行快速-粗糙-实现,就像这个项目中的实现一样:

// TempFileName 为测试或其他用途生成临时文件名
func TempFileName(prefix, suffix string) string {
    randBytes := make([]byte, 16)
    rand.Read(randBytes)
    return filepath.Join(os.TempDir(), prefix+hex.EncodeToString(randBytes)+suffix)
}

正如James Henstridge在下面的评论中所指出的,这是一个粗糙的函数:

> 该函数可能返回已经存在的文件名。例如,这样的API应该通过使用O_CREAT | O_EXCL打开文件来创建文件,以确保在决定名称和创建文件之间没有其他人创建该文件。

上面的粗糙函数仅说明了使用rand.Read()生成文件名的用法。

但是其他检查都在io/ioutil/tempfile.go中。
3of3建议使用math.rand中的一个函数,而不是复制io/ioutil/tempfile.go中的随机数生成器。

英文:

Update (2020: the original answer is from 2015)

As noted in Lax's answer, Go 1.11 (Apr. 2018) has changed TempFile prefix to a pattern.

See commit 191efbc from CL 105675 after issue 4896

> Users of TempFile need to be able to supply the suffix, especially
when using operating systems that give semantic meaning to the
filename extension such as Windows.
Renaming the file to include an extension after the fact is insufficient as it could lead to race conditions.

> If the string given to TempFile includes a "*", the random string
replaces the "*".

> For example "myname.*.bat" will result in a random
filename such as "myname.123456.bat".

> If no "*' is included the
old behavior is retained, and the random digits are appended to the
end.

> If multiple "*" are included, the final one is replaced, thus
permitting a pathological programmer to create filenames such as
"foo*.123456.bat" but not "foo.123456.*.bat"


Original Answer (2015)

> Should I just copy/paste the TempFile code and add in a suffix parameter?

That would be one way.
Other way is to make a quick -- crude -- implementation, as in this project:

// TempFileName generates a temporary filename for use in testing or whatever
func TempFileName(prefix, suffix string) string {
    randBytes := make([]byte, 16)
    rand.Read(randBytes)
    return filepath.Join(os.TempDir(), prefix+hex.EncodeToString(randBytes)+suffix)
}

As James Henstridge comments below, this is a crude function:

> That function can return file names that already exist, for instance. Such an API should be creating the file by opening it with O_CREAT | O_EXCL to ensure that no one else creates the file between deciding on the name and creating the file.

That crude function above illustrates only the use of rand.Read() to generate a filename.

But the other checks are all in io/ioutil/tempfile.go.
3of3 suggests to use a function in math.rand instead of copying the random number generator in io/ioutil/tempfile.go.

答案2

得分: 6

从Go 1.16(2021年2月)开始,您可以使用os.CreateTemp

package main
import "os"

func main() {
   dirs := []string{"", "."}
   for _, dir := range dirs {
      f, err := os.CreateTemp(dir, "*.txt")
      if err != nil {
         panic(err)
      }
      defer f.Close()
      println(f.Name())
   }
}

结果:

C:\Windows\TEMP3991747.txt
.99233563.txt

https://godocs.io/os#CreateTemp

英文:

Starting with Go 1.16 (Feb 2021), you can use os.CreateTemp:

package main
import "os"

func main() {
   dirs := []string{"", "."}
   for _, dir := range dirs {
      f, err := os.CreateTemp(dir, "*.txt")
      if err != nil {
         panic(err)
      }
      defer f.Close()
      println(f.Name())
   }
}

Result:

C:\Windows\TEMP3991747.txt
.99233563.txt

https://godocs.io/os#CreateTemp

答案3

得分: 3

请查看 https://github.com/tink-ab/tempfile。它基本上是标准库提供的内容,但支持前缀和后缀。

英文:

Have a look at https://github.com/tink-ab/tempfile. It's basically what the standard library supplies, but with support for both prefix and suffix.

答案4

得分: 2

对于最近阅读此线程的任何人,截至目前(2020年4月),ioutil.TempFile函数可以同时处理前缀和后缀。

根据其文档:

TempFile在目录dir中创建一个新的临时文件,打开该文件进行读写,并返回生成的*os.File对象。文件名是通过将pattern与随机字符串相加生成的。如果pattern包含“*”,则随机字符串将替换最后一个“*”。

因此,将“prefix-*-suffix”作为pattern参数传递将产生预期的行为。

英文:

To anyone who would read this thread more recently, as of now (April 2020), ioutil.TempFile handles both prefix and suffix.

From its documentation:

> TempFile creates a new temporary file in the directory dir,
> opens the file for reading and writing, and returns the resulting *os.File.
> The filename is generated by taking pattern and adding a random
> string to the end. If pattern includes a "*", the random string
> replaces the last "*".

As such, passing "prefix-*-suffix" as the pattern argument would yield the expected behaviour.

huangapple
  • 本文由 发表于 2015年1月18日 08:24:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/28005865.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定