英文:
io.Writer in Go - beginner trying to understand them
问题
作为Go语言的初学者,我在理解io.Writer
方面遇到了问题。
我的目标是将一个结构体写入到一个JSON文件中。
实现方法如下:
- 使用
encoding/json.Marshal
将结构体转换为字节 - 将这些字节传递给
os.File
的写入器(Writer)
以下是我实现的代码示例:
package main
import (
"os"
"encoding/json"
)
type Person struct {
Name string
Age uint
Occupation []string
}
func MakeBytes(p Person) []byte {
b, _ := json.Marshal(p)
return b
}
func main() {
gandalf := Person{
"Gandalf",
56,
[]string{"sourcerer", "foo fighter"},
}
myFile, err := os.Create("output1.json")
if err != nil {
panic(err)
}
myBytes := MakeBytes(gandalf)
myFile.Write(myBytes)
}
在阅读了这篇文章之后,我将我的程序修改为以下形式:
package main
import (
"io"
"os"
"encoding/json"
)
type Person struct {
Name string
Age uint
Occupation []string
}
// 这个函数的正确名称应该是Write,但为了方便理解,我使用了WriteToFile
func (p *Person) WriteToFile(w io.Writer) {
b, _ := json.Marshal(*p)
w.Write(b)
}
func main() {
gandalf := Person{
"Gandalf",
56,
[]string{"sourcerer", "foo fighter"},
}
myFile, err := os.Create("output2.json")
if err != nil {
panic(err)
}
gandalf.WriteToFile(myFile)
}
在我看来,第一个示例对于初学者来说更直观、更容易理解... 但我有一种感觉,第二个示例是使用Go语言的惯用方式来实现目标。
问题:
- 上述假设正确吗(第二个选项是Go语言的惯用方式)?
- 上述选项有什么区别?哪个选项更好?
- 是否有其他实现相同目标的方法?
谢谢!
WM
英文:
As a beginner in Go, I have problems understanding io.Writer
.
My target: take a struct and write it into a json file.
Approach:
- use
encoding/json.Marshal
to convert my struct into bytes - feed those bytes to an
os.File
Writer
This is how I got it working:
package main
import (
"os"
"encoding/json"
)
type Person struct {
Name string
Age uint
Occupation []string
}
func MakeBytes(p Person) []byte {
b, _ := json.Marshal(p)
return b
}
func main() {
gandalf := Person{
"Gandalf",
56,
[]string{"sourcerer", "foo fighter"},
}
myFile, err := os.Create("output1.json")
if err != nil {
panic(err)
}
myBytes := MakeBytes(gandalf)
myFile.Write(myBytes)
}
After reading this article, I changed my program to this:
package main
import (
"io"
"os"
"encoding/json"
)
type Person struct {
Name string
Age uint
Occupation []string
}
// Correct name for this function would be simply Write
// but I use WriteToFile for my understanding
func (p *Person) WriteToFile(w io.Writer) {
b, _ := json.Marshal(*p)
w.Write(b)
}
func main() {
gandalf := Person{
"Gandalf",
56,
[]string{"sourcerer", "foo fighter"},
}
myFile, err := os.Create("output2.json")
if err != nil {
panic(err)
}
gandalf.WriteToFile(myFile)
}
In my opinion, the first example is a more straightforward and easier to understand for a beginner... but I have the feeling that the 2nd example is the Go idiomatic way of achieving the target.
Questions:
- is above assumption correct (that 2nd option is Go idiomatic) ?
- Is there a difference in the above options ? Which option is better ?
- other ways to achieve the same target ?
Thank you,
WM
答案1
得分: 25
使用第二种方法的好处是,如果你传递一个Writer
接口,你可以传递任何实现了Write
方法的东西,不仅仅是文件,还可以是http.ResponseWriter
,或者是标准输出os.Stdout
,而不需要改变结构体的方法。
你可以查看这篇有用的博文,介绍了io
包的使用方法:io walkthrough。作者认为,将读取器和写入器作为参数传递可以使你的代码更加灵活,部分原因是因为很多函数使用了Reader
和Writer
接口。
当你开始更多地使用Go时,你会注意到标准库有多么依赖于Reader
和Writer
接口,并且可能会对此有所欣赏
所以这个函数(重命名为):
// 将Person的json表示写入到Writer
func (p *Person) WriteJson(w io.Writer) error {
b, err := json.Marshal(*p)
if err != nil {
return err
}
_, err = w.Write(b)
if err != nil {
return err
}
return err
}
可以将数据写入到文件、http响应、用户的标准输出,甚至是一个简单的字节缓冲区,这样测试就会变得更简单。
我将它重命名是因为它的功能;也就是说,这个函数接受一个Person
结构体,并且:
- 将结构体序列化为json表示
- 将json写入到**
Writer
** - 返回序列化/写入过程中出现的任何错误
还有一件事,你可能会对Writer是什么感到困惑,因为它不是一种数据类型,而是一个接口,也就是一种数据类型的行为,一个预定义的方法,一个类型实现的方法。因此,任何实现了Write()
方法的东西都被认为是一个写入器。
对于初学者来说,这可能有点难以理解,但是有很多在线资源可以帮助理解接口(ReadWriter
和Error()
也是一些常见的接口,错误处理也是如此)。
英文:
The benefit of using the second method is that if you are passing a Writer
interface, you can pass anything which implements Write
-- that is not only a file but a http.ResponseWriter
, for example, or stdout os.Stdout
, without changing the struct methods.
You can see this handy blog post on the package io walkthrough. The author makes the case that passing as parameter readers and writers makes your code more flexible, in part because so many functions use the Reader
and Writer
interface.
As you come to use Go more, you'll notice how much the standard library leans on Reader
and Writer
interfaces, and probably come to appreciate it
So this function (renamed):
// writes json representation of Person to Writer
func (p *Person) WriteJson(w io.Writer) error {
b, err := json.Marshal(*p)
if err != nil {
return err
}
_, err = w.Write(b)
if err != nil {
return err
}
return err
}
Would write to a File, http Response, a user's Stdout, or even a simple byte Buffer; making testing a bit simpler.
I renamed it because of what is does; that is, this function takes a Person
struct and:
- Marshals the struct into a json representation
- Writes the json to a
Writer
- Returns any errors arising from marshalling/writing
<hr>
One more thing, you might be confused as to what a Writer is, because it is not a data type, but rather an interface -- that is a behavior of a data type, a predefined method that a type implements. Anything that implements the Write()
method, then, is considered a writer.
This can be a bit difficult for beginners to grasp at first, but there are lots of resources online to help understand interfaces (and ReadWriters
are some of the more common interfaces to encounter, along with Error()
(ei. all errors)).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论