Returning File Pointer in Golang

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

Returning File Pointer in Golang

问题

我仍然在努力理解 Golang 的基础知识。

考虑以下示例代码:

func OpenOutputFile(name string) (fp *os.File) {
  fp, err := os.Create(name)
  if err != nil {
      panic(err)
  }

  defer func() {
      if err := fp.Close(); err != nil {
          panic(err)
      }
  }()

  return fp
}

我会假设调用:

fp := OpenOutputFile("output.txt")

会使 fp 成为一个文件指针 (*os.File),这样我就可以在另一个函数中调用类似这样的语句:

io.WriteString(fp, "Hello World")

但是,在调用这个方法时,会生成错误:

0 write output.txt: bad file descriptor

所以看起来返回的指针无效。我该如何返回一个正确形式的指针以便在 io.WriteString 中使用?

感谢您的帮助!

需要注意的是:当文件指针的创建和写入文件指针存在于同一个方法中时,一切都按预期执行。将逻辑拆分为一个函数会导致它无法按预期工作。

英文:

I'm still struggling with the basics of Golang.

Consider the following sample code:

func OpenOutputFile(name string) (fp *os.File) {
  fp, err := os.Create(name)
  if err != nil {
      panic(err)
  }

  defer func() {
      if err := fp.Close(); err != nil {
          panic(err)
      }
  }()

  return fp
}

I would assume that calling:

fp := OpenOutputFile("output.txt")

would now make fp a file pointer (*os.File), so that I could call a statement like:

io.WriteString(fp, "Hello World")

In another function. But when calling this method, the error is generated:

0 write output.txt: bad file descriptor

So it appears that the pointer returned is not valid. How can I return a properly formed pointer to use with io.WriteString?

I appreciate the help!

Of note: Everything executes as intended when the creation of the file pointer and the writing to the file pointer exists in the same method. Breaking the logic into a function causes it to not behave as intended.

答案1

得分: 7

《Go编程语言规范》

延迟语句

"defer"语句会调用一个函数,该函数的执行被延迟到包围它的函数返回的时刻,无论是因为包围的函数执行了一个返回语句,到达了函数体的末尾,还是因为相应的goroutine正在恐慌。

每次执行"defer"语句时,函数值和调用的参数会像平常一样被求值并重新保存,但实际的函数不会被调用。相反,延迟函数会在包围的函数返回之前立即被调用,按照它们被延迟的相反顺序。如果延迟函数的值求值为nil,在调用函数时会引发恐慌,而不是在执行"defer"语句时。

例如,如果延迟函数是一个函数字面量,并且包围的函数具有在字面量内部可见的命名结果参数,延迟函数可以在返回之前访问和修改结果参数。如果延迟函数有任何返回值,在函数完成时它们会被丢弃。

func OpenOutputFile(name string) (fp *os.File) {
	fp, err := os.Create(name)
	if err != nil {
		panic(err)
	}

	defer func() {
		if err := fp.Close(); err != nil {
			panic(err)
		}
	}()

	return fp
}

你打开文件

fp, err := os.Create(name)

你关闭文件

err := fp.Close()

Close之后,fp不再指向有效的文件描述符。

英文:

> The Go Programming Language Specification
>
> Defer statements
>
> A "defer" statement invokes a function whose execution is deferred to
> the moment the surrounding function returns, either because the
> surrounding function executed a return statement, reached the end of
> its function body, or because the corresponding goroutine is
> panicking.
>
> Each time a "defer" statement executes, the function value and
> parameters to the call are evaluated as usual and saved anew but the
> actual function is not invoked. Instead, deferred functions are
> invoked immediately before the surrounding function returns, in the
> reverse order they were deferred. If a deferred function value
> evaluates to nil, execution panics when the function is invoked, not
> when the "defer" statement is executed.
>
> For instance, if the deferred function is a function literal and the
> surrounding function has named result parameters that are in scope
> within the literal, the deferred function may access and modify the
> result parameters before they are returned. If the deferred function
> has any return values, they are discarded when the function completes.

func OpenOutputFile(name string) (fp *os.File) {
  fp, err := os.Create(name)
  if err != nil {
      panic(err)
  }

  defer func() {
      if err := fp.Close(); err != nil {
          panic(err)
      }
  }()

  return fp
}

You open the file

fp, err := os.Create(name)

You close the file

err := fp.Close()

After the Close, fp no longer points to a valid file descriptor.

答案2

得分: 0

将close函数返回并延迟在更高的作用域中对我起了作用。不过我不确定这是否是Go语言中的一个好实践。它依赖于在原始函数之外关闭/延迟执行。

func OpenFileFromArgs() (*os.File, func()) {
	if len(os.Args) < 2 {
		panic("未提供输入文件")
	}

	inputFilePath := os.Args[1]
	stat, err := os.Stat(inputFilePath)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			panic(fmt.Sprintf("文件 %s 不存在", inputFilePath))
		} else {
			panic(fmt.Sprintf("错误:%v", err))
		}
	}
	if stat.IsDir() {
		panic("提供的路径是一个目录")
	}

	inputFile, err := os.Open(inputFilePath)
	closeFn := func() {
		err := inputFile.Close()
		if err != nil {
			panic("无法关闭输入文件")
		}
	}

	return inputFile, closeFn
}

然后在更高的作用域中:

inputFile, closeFn := library.OpenFileFromArgs()
defer closeFn()

通过逐步调试确认 - 在程序结束时,延迟的关闭函数被正确调用,文件描述符被关闭。

英文:

Returning the close function and deferring it in higher scope worked for me. I don't know if this is a good practice in Go though. It relies on closing/deferring outside of original function:

func OpenFileFromArgs() (*os.File, func()) {
	if len(os.Args) &lt; 2 {
		panic(&quot;input file not provided&quot;)
	}

	inputFilePath := os.Args[1]
	stat, err := os.Stat(inputFilePath)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			panic(fmt.Sprintf(&quot;file %s doens&#39;t exist&quot;, inputFilePath))
		} else {
			panic(fmt.Sprintf(&quot;error: %v&quot;, err))
		}
	}
	if stat.IsDir() {
		panic(&quot;provided path is a directory&quot;)
	}

	inputFile, err := os.Open(inputFilePath)
	closeFn := func() {
		err := inputFile.Close()
		if err != nil {
			panic(&quot;failed to close input file&quot;)
		}
	}

	return inputFile, closeFn

and then in the higher scope:

    inputFile, closeFn := library.OpenFileFromArgs()
	defer closeFn()

Confirmed using step by step debugger - at program end, the deferred close function is correctly called and file descriptor closed.

huangapple
  • 本文由 发表于 2016年3月20日 11:22:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/36109887.html
匿名

发表评论

匿名网友

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

确定