mirror of
https://github.com/unknwon/the-way-to-go_ZH_CN.git
synced 2025-08-12 03:06:41 +08:00
翻译11.12部分内容
This commit is contained in:
@@ -35,6 +35,7 @@
|
|||||||
- [@spawnris](https://github.com/spawnris)
|
- [@spawnris](https://github.com/spawnris)
|
||||||
- [@domainname](https://github.com/domainname)
|
- [@domainname](https://github.com/domainname)
|
||||||
- [@leisore](https://github.com/leisore)
|
- [@leisore](https://github.com/leisore)
|
||||||
|
- [@dake](https://github.com/dake)
|
||||||
|
|
||||||
## 授权许可
|
## 授权许可
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
- [@spawnris](https://github.com/spawnris)
|
- [@spawnris](https://github.com/spawnris)
|
||||||
- [@domainname](https://github.com/domainname)
|
- [@domainname](https://github.com/domainname)
|
||||||
- [@leisore](https://github.com/leisore)
|
- [@leisore](https://github.com/leisore)
|
||||||
|
- [@dake](https://github.com/dake)
|
||||||
|
|
||||||
## 适用人群
|
## 适用人群
|
||||||
|
|
||||||
|
@@ -44,7 +44,7 @@ Go 语言中的接口都很简短,通常它们会包含 0 个、最多 3 个
|
|||||||
|
|
||||||
第一个例子:
|
第一个例子:
|
||||||
|
|
||||||
示例 11.1 interfaces.go:
|
示例 11.1 [interfaces.go](examples/chapter_11/interfaces.go):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -100,7 +100,7 @@ func main() {
|
|||||||
|
|
||||||
扩展一下上面的例子,类型 `Rectangle` 也实现了 `Shaper` 接口。接着创建一个 `Shaper` 类型的数组,迭代它的每一个元素并在上面调用 `Area()` 方法,以此来展示多态行为:
|
扩展一下上面的例子,类型 `Rectangle` 也实现了 `Shaper` 接口。接着创建一个 `Shaper` 类型的数组,迭代它的每一个元素并在上面调用 `Area()` 方法,以此来展示多态行为:
|
||||||
|
|
||||||
示例 11.2 interfaces_poly.go:
|
示例 11.2 [interfaces_poly.go](examples/chapter_11/interfaces_poly.go):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -156,7 +156,7 @@ func main() {
|
|||||||
|
|
||||||
下面是一个更具体的例子:有两个类型 `stockPosition` 和 `car`,它们都有一个 `getValue()` 方法,我们可以定义一个具有此方法的接口 `valuable`。接着定义一个使用 `valuable` 类型作为参数的函数 `showValue()`,所有实现了 `valuable` 接口的类型都可以用这个函数。
|
下面是一个更具体的例子:有两个类型 `stockPosition` 和 `car`,它们都有一个 `getValue()` 方法,我们可以定义一个具有此方法的接口 `valuable`。接着定义一个使用 `valuable` 类型作为参数的函数 `showValue()`,所有实现了 `valuable` 接口的类型都可以用这个函数。
|
||||||
|
|
||||||
示例 11.3 valuable.go:
|
示例 11.3 [valuable.go](examples/chapter_11/valuable.go):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
228
eBook/11.12.md
228
eBook/11.12.md
@@ -0,0 +1,228 @@
|
|||||||
|
# 11.12 接口与动态类型
|
||||||
|
|
||||||
|
## 11.12.1 Go的动态类型
|
||||||
|
|
||||||
|
在经典的 OO 语言(像 C++,Java 和 C#)中数据和方法被封装为 `类的概念`:类包含它们全部,并且不能分开。
|
||||||
|
|
||||||
|
Go 没有类:数据(结构体或更一般的类型)和方法是正交关系,一种松耦合。
|
||||||
|
|
||||||
|
Go 中的接口跟 Java/C# 类似:都是必须提供一个指定方法集的实现。但是更灵活通用:任何提供了接口方法实现代码的类型都隐式地实现了该接口,而不用显式地声明。
|
||||||
|
|
||||||
|
和其它语言相比,Go 是唯一结合了接口值,静态类型检查(是否该类型实现了某个接口),运行时动态转换的语言,并且不需要显式地声明类型是否满足某个接口。该特性允许我们定义和使用新接口,而不用改变已有的代码。
|
||||||
|
|
||||||
|
以一个(或多个)接口类型作为参数的函数,可以被实现了该接口的类型的实例调用。`实现了某个接口的类型可以被传给任何以此接口为参数的函数`。
|
||||||
|
|
||||||
|
类似于 Python 和 Ruby 这类动态语言中的 `动态类型(duck typing)`;这意味着对象可以根据提供的方法被处理(例如,作为参数传递给函数),而忽略它们的实际类型:它们能做什么比它们是什么更重要。
|
||||||
|
|
||||||
|
这在程序 duck_dance.go 中得以阐明,函数 DuckDance 接受一个 IDuck 的接口类型变量。仅当 DuckDance 是被实现了 IDuck 接口的类型调用时程序才能通过编译。
|
||||||
|
|
||||||
|
示例 11.16 [duck_dance.go](examples/chapter_11/duck_dance.go):
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type IDuck interface {
|
||||||
|
Quack()
|
||||||
|
Walk()
|
||||||
|
}
|
||||||
|
|
||||||
|
func DuckDance(duck IDuck) {
|
||||||
|
for i := 1; i <= 3; i++ {
|
||||||
|
duck.Quack()
|
||||||
|
duck.Walk()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bird struct {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bird) Quack() {
|
||||||
|
fmt.Println("I am quacking!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bird) Walk() {
|
||||||
|
fmt.Println("I am walking!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
b := new(Bird)
|
||||||
|
DuckDance(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Output:
|
||||||
|
I am quacking!
|
||||||
|
I am walking!
|
||||||
|
I am quacking!
|
||||||
|
I am walking!
|
||||||
|
I am quacking!
|
||||||
|
I am walking!
|
||||||
|
*/
|
||||||
|
|
||||||
|
```
|
||||||
|
如果 `Bird` 没有实现 `Walk()`(把它注释掉),会得到一个编译错误:
|
||||||
|
|
||||||
|
```
|
||||||
|
cannot use b (type *Bird) as type IDuck in function argument:
|
||||||
|
*Bird does not implement IDuck (missing Walk method)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
如果对 `cat` 调用函数 `DuckDance()`,Go 会提示编译错误,但是 Python 和 Ruby 会以运行时错误结束。
|
||||||
|
|
||||||
|
## 11.12.2 动态方法调用
|
||||||
|
|
||||||
|
像 Python,Ruby 这类语言,动态类型是延迟绑定的(在运行时进行):方法只是用参数和变量简单地调用,然后在运行时才解析(它们很可能有像 `responds_to` 这样的方法来检查对象是否可以响应某个方法,但是这也意味着更大的编码量和更多的测试)
|
||||||
|
|
||||||
|
Go 的实现与此相反,通常需要编译器静态检查的支持:当变量被赋值给一个接口类型的变量时,编译器会检查其是否实现了该接口的所有函数。如果方法调用作用于像 `interface{}` 这样的“泛型”上,你可以通过类型断言(参见 11.3 节)来检查变量是否实现了相应接口。
|
||||||
|
|
||||||
|
例如,假设你用不同的类型表示 XML 输出流中的不同实体。然后我们为 XML 定义一个如下的“写”接口(甚至可以把它定义为私有接口):
|
||||||
|
|
||||||
|
```go
|
||||||
|
type xmlWriter interface {
|
||||||
|
WriteXML(w io.Writer) error
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
现在我们可以实现适用于流类型的任何变量的 `StreamXML` 函数,并用类型断言检查传入的变量是否实现了该接口;如果没有,我们就调用内建的 `encodeToXML` 来完成相应工作:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Exported XML streaming function.
|
||||||
|
func StreamXML(v interface{}, w io.Writer) error {
|
||||||
|
if xw, ok := v.(xmlWriter); ok {
|
||||||
|
// It’s an xmlWriter, use method of asserted type.
|
||||||
|
return xw.WriteXML(w)
|
||||||
|
}
|
||||||
|
// No implementation, so we have to use our own function (with perhaps reflection):
|
||||||
|
return encodeToXML(v, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal XML encoding function.
|
||||||
|
func encodeToXML(v interface{}, w io.Writer) error {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Go 在这里用了和 `gob` 相同的机制:定义了两个接口 `GobEncoder` 和 `GobDecoder`。这样就允许类型自己实现从流编解码的方式;如果没有就使用标准的反射方式。
|
||||||
|
|
||||||
|
因此 Go 提供了动态语言的优点,却没有其他动态语言在运行时可能发生错误的缺点。
|
||||||
|
|
||||||
|
对于动态语言非常重要的单元测试来说,这样即可以减少单元测试的部分需求,又可以发挥相当大的作用。
|
||||||
|
|
||||||
|
Go 的接口提高了代码的分离度,改善了代码的复用性,使得代码开发过程中的设计模式更容易实现。用 Go 接口还能实现 `依赖注入模式`。
|
||||||
|
|
||||||
|
## 11.12.3 接口的提取
|
||||||
|
|
||||||
|
`提取接口` 是非常有用的设计模式,可以减少需要的类型和方法数量,而且不需要像传统的基于类的 OO 语言那样维护整个的类层次结构。
|
||||||
|
|
||||||
|
Go 接口可以让开发者找出自己写的程序中的类型。假设有一些拥有共同行为的对象,并且开发者想要抽象出这些行为,这时就可以创建一个接口来使用。
|
||||||
|
我们来扩展 11.1 节的示例 11.2 interfaces_poly.go,假设我们发现我们需要一个新的接口 `TopologicalGenus`,用来给 shape 排序(这里简单地实现为返回 int)。我们需要做的是给想要满足接口的类型实现 `Rank()` 方法:
|
||||||
|
|
||||||
|
示例 11.17 [multi_interfaces_poly.go](examples/chapter_11/multi_interfaces_poly.go):
|
||||||
|
|
||||||
|
```go
|
||||||
|
//multi_interfaces_poly.go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Shaper interface {
|
||||||
|
Area() float32
|
||||||
|
}
|
||||||
|
|
||||||
|
type TopologicalGenus interface {
|
||||||
|
Rank() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Square struct {
|
||||||
|
side float32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sq *Square) Area() float32 {
|
||||||
|
return sq.side * sq.side
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sq *Square) Rank() int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rectangle struct {
|
||||||
|
length, width float32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Rectangle) Area() float32 {
|
||||||
|
return r.length * r.width
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Rectangle) Rank() int {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := Rectangle{5, 3} // Area() of Rectangle needs a value
|
||||||
|
q := &Square{5} // Area() of Square needs a pointer
|
||||||
|
shapes := []Shaper{r, q}
|
||||||
|
fmt.Println("Looping through shapes for area ...")
|
||||||
|
for n, _ := range shapes {
|
||||||
|
fmt.Println("Shape details: ", shapes[n])
|
||||||
|
fmt.Println("Area of this shape is: ", shapes[n].Area())
|
||||||
|
}
|
||||||
|
topgen := []TopologicalGenus{r, q}
|
||||||
|
fmt.Println("Looping through topgen for rank ...")
|
||||||
|
for n, _ := range topgen {
|
||||||
|
fmt.Println("Shape details: ", topgen[n])
|
||||||
|
fmt.Println("Topological Genus of this shape is: ", topgen[n].Rank())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Output:
|
||||||
|
Looping through shapes for area ...
|
||||||
|
Shape details: {5 3}
|
||||||
|
Area of this shape is: 15
|
||||||
|
Shape details: &{5}
|
||||||
|
Area of this shape is: 25
|
||||||
|
Looping through topgen for rank ...
|
||||||
|
Shape details: {5 3}
|
||||||
|
Topological Genus of this shape is: 2
|
||||||
|
Shape details: &{5}
|
||||||
|
Topological Genus of this shape is: 1
|
||||||
|
*/
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
所以你不用提前设计出所有的接口;`整个设计可以持续演进,而不用废弃之前的决定`。当类型要实现一个接口,它本身不用改变,你只需要在这个类型上实现新的方法。
|
||||||
|
|
||||||
|
## 11.12.4 显式地指明类型实现了某个接口
|
||||||
|
|
||||||
|
如果你希望满足某个接口的类型显式地声明它们实现了这个接口,你可以向接口的方法集中添加一个具有描述性名字的方法。例如:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Fooer interface {
|
||||||
|
Foo()
|
||||||
|
ImplementsFooer()
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
类型 Bar 必须实现 `ImplementsFooer` 方法来满足 `Footer` 接口,以清楚地记录这个事实。
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Bar struct{}
|
||||||
|
func (b Bar) ImplementsFooer() {} func (b Bar) Foo() {}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
大部分代码并不使用这样的约束,因为它限制了接口的实用性。
|
||||||
|
|
||||||
|
但是有些时候,这样的约束在大量相似的接口中被用来解决歧义。
|
||||||
|
|
||||||
|
## 11.12.5 空接口和函数重载
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -30,7 +30,7 @@ if _, ok := varI.(T); ok {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
示例 11.4 type_interfaces.go
|
示例 11.4 [type_interfaces.go](examples/chapter_11/type_interfaces.go):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
在第 10.6.3 节及例子 methodset1.go 中我们看到,作用于变量上的方法实际上是不区分变量到底是指针还是值的。当碰到接口类型值时,这会变得有点复杂,原因是接口变量中存储的具体值是不可寻址的,幸运的是,如果使用不当编译器会给出错误。考虑下面的程序:
|
在第 10.6.3 节及例子 methodset1.go 中我们看到,作用于变量上的方法实际上是不区分变量到底是指针还是值的。当碰到接口类型值时,这会变得有点复杂,原因是接口变量中存储的具体值是不可寻址的,幸运的是,如果使用不当编译器会给出错误。考虑下面的程序:
|
||||||
|
|
||||||
示例 11.5 methodset2.go:
|
示例 11.5 [methodset2.go](examples/chapter_11/methodset2.go):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
@@ -49,7 +49,7 @@ sort.Sort(a)
|
|||||||
|
|
||||||
同样的原理,排序函数可以用于一个浮点型数组,一个字符串数组,或者一个表示每周各天的结构体 `dayArray`.
|
同样的原理,排序函数可以用于一个浮点型数组,一个字符串数组,或者一个表示每周各天的结构体 `dayArray`.
|
||||||
|
|
||||||
示例 11.6 sort.go:
|
示例 11.6 [sort.go](examples/chapter_11/sort.go):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package sort
|
package sort
|
||||||
@@ -101,7 +101,7 @@ func IntsAreSorted(a []int) bool { return IsSorted(IntArray(a)) }
|
|||||||
func StringsAreSorted(a []string) bool { return IsSorted(StringArray(a)) }
|
func StringsAreSorted(a []string) bool { return IsSorted(StringArray(a)) }
|
||||||
```
|
```
|
||||||
|
|
||||||
示例 11.7 sortmain.go:
|
示例 11.7 [sortmain.go](examples/chapter_11/sortmain.go):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
@@ -14,7 +14,7 @@ type Any interface {}
|
|||||||
|
|
||||||
可以给一个空接口类型的变量 `var val interface {}` 赋任何类型的值。
|
可以给一个空接口类型的变量 `var val interface {}` 赋任何类型的值。
|
||||||
|
|
||||||
示例 11.8 empty_interface.go:
|
示例 11.8 [empty_interface.go](examples/chapter_11/empty_interface.go):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -65,7 +65,7 @@ func main() {
|
|||||||
|
|
||||||
在上面的例子中,接口变量 `val` 被依次赋予一个 `int`,`string` 和 `Person` 实例的值,然后使用 `type-swtich` 来测试它的实际类型。每个 `interface {}` 变量在内存中占据两个字长:一个用来存储它包含的类型,另一个用来存储它包含的数据或者指向数据的指针。
|
在上面的例子中,接口变量 `val` 被依次赋予一个 `int`,`string` 和 `Person` 实例的值,然后使用 `type-swtich` 来测试它的实际类型。每个 `interface {}` 变量在内存中占据两个字长:一个用来存储它包含的类型,另一个用来存储它包含的数据或者指向数据的指针。
|
||||||
|
|
||||||
例子 emptyint_switch.go 说明了空接口在 `type-swtich` 中联合 `lambda` 函数的用法:
|
示例 [emptyint_switch.go](examples/chapter_11/emptyint_switch.go) 说明了空接口在 `type-swtich` 中联合 `lambda` 函数的用法:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -172,7 +172,7 @@ for ix, d := range dataSlice {
|
|||||||
|
|
||||||
在10.1中我们遇到了诸如列表和树这样的数据结构,在它们的定义中使用了一种叫节点的递归结构体类型,节点包含一个某种类型的数据字段。现在可以使用空接口作为数据字段的类型,这样我们就能写出通用的代码。下面是实现一个二叉树的部分代码:通用定义、用于创建空节点的 `NewNode` 方法,及设置数据的 `SetData` 方法.
|
在10.1中我们遇到了诸如列表和树这样的数据结构,在它们的定义中使用了一种叫节点的递归结构体类型,节点包含一个某种类型的数据字段。现在可以使用空接口作为数据字段的类型,这样我们就能写出通用的代码。下面是实现一个二叉树的部分代码:通用定义、用于创建空节点的 `NewNode` 方法,及设置数据的 `SetData` 方法.
|
||||||
|
|
||||||
示例 11.10 node_structures.go:
|
示例 11.10 [node_structures.go](examples/chapter_11/node_structures.go):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# 读写数据
|
# 12 读写数据
|
||||||
|
|
||||||
除了 fmt 和 os 包,我们还需要用到 bufio 包来处理缓冲的输入和输出。
|
除了 fmt 和 os 包,我们还需要用到 bufio 包来处理缓冲的输入和输出。
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# 读取用户的输入
|
# 12.1 读取用户的输入
|
||||||
|
|
||||||
我们如何读取用户的键盘(控制台)输入呢?从键盘和标准输入 `os.Stdin` 读取输入,最简单的办法是使用 `fmt` 包提供的 Scan 和 Sscan 开头的函数。请看以下程序:
|
我们如何读取用户的键盘(控制台)输入呢?从键盘和标准输入 `os.Stdin` 读取输入,最简单的办法是使用 `fmt` 包提供的 Scan 和 Sscan 开头的函数。请看以下程序:
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
# XML 数据格式
|
# 12.10 XML 数据格式
|
||||||
|
|
||||||
下面是与 12.9 节 JSON 例子等价的 XML 版本:
|
下面是与 12.9 节 JSON 例子等价的 XML 版本:
|
||||||
```xml
|
```xml
|
||||||
<Person>
|
<Person>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# 用 Gob 传输数据
|
# 12.11 用 Gob 传输数据
|
||||||
|
|
||||||
Gob 是 Go 自己的以二进制形式序列化和反序列化程序数据的格式;可以在 `encoding` 包中找到。这种格式的数据简称为 Gob (即 Go binary 的缩写)。类似于 Python 的 "pickle" 和 Java 的 "Serialization"。
|
Gob 是 Go 自己的以二进制形式序列化和反序列化程序数据的格式;可以在 `encoding` 包中找到。这种格式的数据简称为 Gob (即 Go binary 的缩写)。类似于 Python 的 "pickle" 和 Java 的 "Serialization"。
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Go 中的密码学
|
# 12.12 Go 中的密码学
|
||||||
|
|
||||||
通过网络传输的数据必须加密,以防止被 hacker(黑客)读取或篡改,并且保证发出的数据和收到的数据检验和一致。
|
通过网络传输的数据必须加密,以防止被 hacker(黑客)读取或篡改,并且保证发出的数据和收到的数据检验和一致。
|
||||||
鉴于 Go 母公司的业务,我们毫不惊讶地看到 Go 的标准库为该领域提供了超过 30 个的包:
|
鉴于 Go 母公司的业务,我们毫不惊讶地看到 Go 的标准库为该领域提供了超过 30 个的包:
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# 文件读写
|
# 12.2 文件读写
|
||||||
|
|
||||||
## 12.2.1 读文件
|
## 12.2.1 读文件
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
# 文件拷贝
|
# 12.3 文件拷贝
|
||||||
|
|
||||||
如何拷贝一个文件到另一个文件?最简单的方式就是使用 io 包:
|
如何拷贝一个文件到另一个文件?最简单的方式就是使用 io 包:
|
||||||
|
|
||||||
示例 12.10 [filecopy.go](examples/chapter_12/filecopy.go):
|
示例 12.10 [filecopy.go](examples/chapter_12/filecopy.go):
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
# 从命令行读取参数
|
# 12.4 从命令行读取参数
|
||||||
|
|
||||||
## 12.4.1 os 包
|
## 12.4.1 os 包
|
||||||
os 包中有一个 string 类型的切片变量 `os.Args`,用来处理一些基本的命令行参数,它在程序启动后读取命令行输入的参数。来看下面的打招呼程序:
|
os 包中有一个 string 类型的切片变量 `os.Args`,用来处理一些基本的命令行参数,它在程序启动后读取命令行输入的参数。来看下面的打招呼程序:
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# 用 buffer 读取文件
|
# 12.5 用 buffer 读取文件
|
||||||
|
|
||||||
在下面的例子中,我们结合使用了缓冲读取文件和命令行 flag 解析这两项技术。如果不加参数,那么你输入什么屏幕就打印什么。
|
在下面的例子中,我们结合使用了缓冲读取文件和命令行 flag 解析这两项技术。如果不加参数,那么你输入什么屏幕就打印什么。
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# 用切片读写文件
|
# 12.6 用切片读写文件
|
||||||
|
|
||||||
切片提供了 Go 中处理 I/O 缓冲的标准方式,下面 `cat` 函数的第二版中,在一个切片缓冲内使用无限 for 循环(直到文件尾部 EOF)读取文件,并写入到标准输出(`os.Stdout`)。
|
切片提供了 Go 中处理 I/O 缓冲的标准方式,下面 `cat` 函数的第二版中,在一个切片缓冲内使用无限 for 循环(直到文件尾部 EOF)读取文件,并写入到标准输出(`os.Stdout`)。
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# 用 defer 关闭文件
|
# 12.7 用 defer 关闭文件
|
||||||
|
|
||||||
`defer` 关键字(参看 6.4)对于在函数结束时关闭打开的文件非常有用,例如下面的代码片段:
|
`defer` 关键字(参看 6.4)对于在函数结束时关闭打开的文件非常有用,例如下面的代码片段:
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# 一个使用接口的实际例子:fmt.Fprintf
|
# 12.8 一个使用接口的实际例子:fmt.Fprintf
|
||||||
|
|
||||||
例子程序 `io_interfaces.go` 很好的阐述了 io 包中的接口概念。
|
例子程序 `io_interfaces.go` 很好的阐述了 io 包中的接口概念。
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Json 数据格式
|
# 12.9 Json 数据格式
|
||||||
|
|
||||||
数据结构要在网络中传输或保存到文件,就必须对其编码和解码;目前存在很多编码格式:JSON,XML,gob,Google 缓冲协议等等。Go 语言支持所有这些编码格式;在后面的章节,我们将讨论前三种格式。
|
数据结构要在网络中传输或保存到文件,就必须对其编码和解码;目前存在很多编码格式:JSON,XML,gob,Google 缓冲协议等等。Go 语言支持所有这些编码格式;在后面的章节,我们将讨论前三种格式。
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user