Merge pull request #79 from domainname/master

bug fixes
This commit is contained in:
无闻
2015-03-16 22:15:57 -04:00
8 changed files with 346 additions and 153 deletions

View File

@@ -1,6 +1,6 @@
# 9.1 标准库概述 # 9.1 标准库概述
像 fmtos 等这样具有常用功能的内置包在 Go 语言中有 150 个以上,它们被称为标准库,大部分(一些底层的除外)内置于 Go 本身。记录在: http://golang.org/pkg/。 像 fmtos 等这样具有常用功能的内置包在 Go 语言中有 150 个以上,它们被称为标准库,大部分(一些底层的除外)内置于 Go 本身。记录在: [http://golang.org/pkg/](http://golang.org/pkg/)
在贯穿本书的例子和练习中,我们都是用标准库的包。可以通过查阅第 350 页包中的内容快速找到相关的包的实例。这里我们只是按功能进行分组来介绍这些包的简单用途,我们不会深入讨论他们的内部结构。 在贯穿本书的例子和练习中,我们都是用标准库的包。可以通过查阅第 350 页包中的内容快速找到相关的包的实例。这里我们只是按功能进行分组来介绍这些包的简单用途,我们不会深入讨论他们的内部结构。

Binary file not shown.

View File

@@ -24,7 +24,10 @@ func ReturnStr() string {
```go ```go
import ./pack1/pack1 import ./pack1/pack1
``` ```
import 的一般格式如下:
import “包的路径或url地址“ 像 import "github.com/org1/pack1” import “包的路径或url地址“ 像 import "github.com/org1/pack1”
路径是指当前目录的相对路径。 路径是指当前目录的相对路径。
@@ -43,31 +46,37 @@ func main() {
var test1 string var test1 string
test1 = pack1.ReturnStr() test1 = pack1.ReturnStr()
fmt.Printf("ReturnStr from package1: %s\n", test1) fmt.Printf("ReturnStr from package1: %s\n", test1)
var test1 string fmt.Printf(Integer from package1: %d\n, pack1.Pack1Int)
// fmt.Printf(“Float from package1: %f\n”, pack1.pack1Float)
} }
``` ```
输出结果: 输出结果:
// fmt.Printf(“Float from package1: %f\n”, pack1.pack1Float)
ReturnStr from package1: Hello main!
Integer from package1: 42
如果包 pack1 和我们的程序在统同一路径下,我们可以通过 `"import ./pack1"` 这样的方式来引入,但这不被视为一个好的方法。 如果包 pack1 和我们的程序在统同一路径下,我们可以通过 `"import ./pack1"` 这样的方式来引入,但这不被视为一个好的方法。
`fmt.Printf(“Float from package1: %f\n”, pack1.pack1Float)` 这行代码试图访问一个未引用的变量或者函数,甚至没有编译。将会返回一个错误: `fmt.Printf(“Float from package1: %f\n”, pack1.pack1Float)` 这行代码试图访问一个未引用的变量或者函数,甚至没有编译。将会返回一个错误:
cannot refer to unexported name pack1.pack1Float cannot refer to unexported name pack1.pack1Float
主程序利用的包必须在主程序编写之前被编译。主程序中每个 pack1 项目都要通过包名来使用使用pack1.Item。具体使用方法请参见示例 4.6 和 4.7。 主程序利用的包必须在主程序编写之前被编译。主程序中每个 pack1 项目都要通过包名来使用使用pack1.Item。具体使用方法请参见示例 4.6 和 4.7。
因此,按照惯例子目录和包之间有着密切的联系:为了区分不同包存放在不同的目录,每个包(所有属于这个包中的 go 文件)都存放在和包名相同的子目录下: 因此,按照惯例子目录和包之间有着密切的联系:为了区分不同包存放在不同的目录,每个包(所有属于这个包中的 go 文件)都存放在和包名相同的子目录下:
**Import with .** : import . “./pack1”
当使用.来做为包的别名时,你可以不通过包名来使用其中的项目。例如:`test := ReturnStr()` 当使用.来做为包的别名时,你可以不通过包名来使用其中的项目。例如:`test := ReturnStr()`
在当前的命名空间导入 pack1 包,一般是为了具有更好的测试效果。 在当前的命名空间导入 pack1 包,一般是为了具有更好的测试效果。
**Import with _** : import _ “./pack1/pack1”
pack1包只导入其副作用也就是说只执行它的init函数并初始化其中的全局变量。
**导入外部安装包:**
如果你要在你的应用中使用一个或多个外部包,首先你必须使用 go install(参见第 9.7 节)在你的本地机器上安装它们。 如果你要在你的应用中使用一个或多个外部包,首先你必须使用 go install(参见第 9.7 节)在你的本地机器上安装它们。
@@ -83,11 +92,11 @@ pack1包只导入其副作用只执行了它的init函数并初始化了其
import goex “codesite.ext/author/goExample/goex” import goex “codesite.ext/author/goExample/goex”
因此该包的URL将用作导入路径。
在 http://golang.org/cmd/goinstall/ 的 go install 文档中列出了一些广泛被使用的托管在网络代码仓库的包的导入路径 在 http://golang.org/cmd/goinstall/ 的 go install 文档中列出了一些广泛被使用的托管在网络代码仓库的包的导入路径
**包的初始化:**
程序的执行开始于导入包,初始化 main 包然后调用 main 函数。 程序的执行开始于导入包,初始化 main 包然后调用 main 函数。
@@ -97,23 +106,33 @@ init 函数是不能被调用的。
导入的包在包自身初始化前被初始化,而一个包在程序执行中只能初始化一次。 导入的包在包自身初始化前被初始化,而一个包在程序执行中只能初始化一次。
**编译并安装一个包(参见第 9.7 节):**
在 Linux/OS X 下可以用类似第 4.3 节的 Makefile 脚本做到这一点: 在 Linux/OS X 下可以用类似第 4.3 节的 Makefile 脚本做到这一点:
include $(GOROOT)/src/Make.inc
TARG=pack1
GOFILES=\
pack1.go\
pack1b.go\
include $(GOROOT)/src/Make.pkg
确保的它可执行性通过 `chmod 777 ./Makefile` 确保的它可执行性通过 `chmod 777 ./Makefile`
导入的包在包自身初始化前被初始化,而一个包在程序执行中只能初始化一次 上面脚本内的include语引入了相应的功能将自动检测机器的架构并调用正确的编译器和链接器
编译并安装一个包(参见第 9.7 节): 然后终端执行 make 或 `gomake` 工具:他们都会生成一个包含静态库 pack1.a 的 _obj 目录。
go install(参见第 9.7 节,从 Go1 的首选方式)同样复制 pack1.a 到本地的 $GOROOT/pkg 的目录中一个以操作系统为名的子目录下。像 import "pack1" 代替 import "path to pack1",这样只通过名字就可以将包在程序中导入。 go install(参见第 9.7 节,从 Go1 的首选方式)同样复制 pack1.a 到本地的 $GOROOT/pkg 的目录中一个以操作系统为名的子目录下。像 import "pack1" 代替 import "path to pack1",这样只通过名字就可以将包在程序中导入。
如果不可取或不被允许,通过 6/8g 使用 -I 选项来编译: 如果不可取或不被允许,通过 6/8g 使用 -I 选项来编译:
GOFILES=\ 6g—I map_pack1 package_test.go # where map_pack1 is the map which contains pack1.a
(I 选项让编译器查找选项后的目录下是否包含这个包)
使用 6/8l 的 -L 选项链接: 使用 6/8l 的 -L 选项链接:
6l—L map_pack1 package_test.6 6l—L map_pack1 package_test.6
当第 13 章我们遇到使用测试工具进行测试的时候我们将重新回到自己的包的制作和编译这个话题。 当第 13 章我们遇到使用测试工具进行测试的时候我们将重新回到自己的包的制作和编译这个话题。
@@ -135,7 +154,7 @@ go install(参见第 9.7 节,从 Go1 的首选方式)同样复制 pack1.a 到
练习 9.4:创建一个程序 main_oddven.go 判断前 100 个整数是不是偶数,包内同时包含测试的功能。 练习 9.4:创建一个程序 main_oddven.go 判断前 100 个整数是不是偶数,包内同时包含测试的功能。
当第 13 章我们遇到使用测试工具进行测试的时候我们将重新回到自己的包的制作和编译这个话题。 练习 9.5:使用第 6.6 节的斐波那契程序:
(1)将斐波那契功能放入自己的 fibo 包中并通过主程序调用它,存储最后输入的值在函数的全局变量。 (1)将斐波那契功能放入自己的 fibo 包中并通过主程序调用它,存储最后输入的值在函数的全局变量。

View File

@@ -4,7 +4,7 @@ go install 是 Go 中自动包安装工具:如需要将包安装到本地它
在包安装前的先决条件是要自动处理包自身依赖关系的安装。被依赖的包也会安装到子目录下,但是没有文档和示例:可以到网上浏览。 在包安装前的先决条件是要自动处理包自身依赖关系的安装。被依赖的包也会安装到子目录下,但是没有文档和示例:可以到网上浏览。
被安装包的列表可以在 $GORROT/goinstall.log 找到。 被安装包的列表可以在 $GOROOT/goinstall.log 找到。
go install 使用了 GOPATH 变量(详见第 2.2 节)。 go install 使用了 GOPATH 变量(详见第 2.2 节)。

9
eBook/12.0.md Normal file
View File

@@ -0,0 +1,9 @@
# 读写数据
除了 fmt 和 os 包,我们还需要用到 bufio 包来处理缓冲的输入和输出。
## 链接
- [目录](directory.md)
- 上一章:[接口interface与反射reflection](11.0.md)
- 下一节:[读取用户的输入](12.1.md)

151
eBook/12.1.md Normal file
View File

@@ -0,0 +1,151 @@
# 读取用户的输入
我们如何读取用户的键盘(控制台)输入呢?从键盘和标准输入 `os.Stdin` 读取输入,最简单的办法是使用 `fmt` 包提供的 Scan 和 Sscan 开头的函数。请看以下程序:
**Listing 12.1—readinput1.go:**
```go
// 从控制台读取输入:
package main
import "fmt"
var (
firstName, lastName, s string
i int
f float32
input = "56.12 / 5212 / Go"
format = "%f / %d / %s"
)
func main() {
fmt.Println("Please enter your full name: ")
fmt.Scanln(&firstName, &lastName)
// fmt.Scanf("%s %s", &firstName, &lastName)
fmt.Printf("Hi %s %s!\n", firstName, lastName) // Hi Chris Naegels
fmt.Sscanf(input, format, &f, &i, &s)
fmt.Println("From the string we read: ", f, i, s)
// 输出结果: From the string we read: 56.12 5212 Go
}
```
`Scanln` 扫描来自标准输入的文本,将空格分隔的值依次存放到后续的参数内,直到碰到换行。`Scanf` 与其类似,除了 `Scanf` 的第一个参数用作格式字符串,用来决定如何读取。`Sscan` 和以 `Sscan` 开头的函数则是从字符串读取,除此之外,与 `Scanf` 相同。如果这些函数读取到的结果与您预想的不同,您可以检查成功读入数据的个数和返回的错误。
您也可以使用 `bufio` 包提供的缓冲读取buffered reader来读取数据正如以下例子所示
**Listing 12.2—readinput2.go:**
```go
package main
import (
"fmt"
"bufio"
"os"
)
var inputReader *bufio.Reader
var input string
var err error
func main() {
inputReader = bufio.NewReader(os.Stdin)
fmt.Println("Please enter some input: ")
input, err = inputReader.ReadString('\n')
if err == nil {
fmt.Printf("The input was: %s\n", input)
}
}
```
`inputReader` 是一个指向 `bufio.Reader` 的指针。`inputReader := bufio.NewReader(os.Stdin)` 这行代码,将会创建一个读取器,并将其与标准输入绑定。
`bufio.NewReader()` 构造函数的签名为:`func NewReader(rd io.Reader) *Reader`
该函数的实参可以是满足 `io.Reader` 接口的任意对象(任意包含有适当的 `Read()` 方法的对象,请参考[章节11.8](11.8.md)),函数返回一个新的带缓冲的 `io.Reader` 对象,它将从指定读取器(例如 `os.Stdin`)读取内容。
返回的读取器对象提供一个方法 `ReadString(delim byte)`,该方法从输入中读取内容,直到碰到 `delim` 指定的字符,然后将读取到的内容连同 `delim` 字符一起放到缓冲区。
`ReadString` 返回读取到的字符串,如果碰到错误则返回 `nil`。如果它一直读到文件结束,则返回读取到的字符串和 `io.EOF`。如果读取过程中没有碰到 `delim` 字符,将返回错误 `err != nil`
在上面的例子中,我们会读取键盘输入,直到回车键(\n被按下。
屏幕是标准输出 `os.Stdout``os.Stderr` 用于显示错误信息,大多数情况下等同于 `os.Stdout`
一般情况下,我们会省略变量声明,而使用 `:=`,例如:
```go
inputReader := bufio.NewReader(os.Stdin)
input, err := inputReader.ReadString('\n')
```
我们将从现在开始使用这种写法。
第二个例子从键盘读取输入,使用了 `switch` 语句:
**Listing 12.3—switch_input.go:**
```go
package main
import (
"fmt"
"os"
"bufio"
)
func main() {
inputReader := bufio.NewReader(os.Stdin)
fmt.Println("Please enter your name:")
input, err := inputReader.ReadString('\n')
if err != nil {
fmt.Println("There were errors reading, exiting program.")
return
}
fmt.Printf("Your name is %s", input)
// For Unix: test with delimiter "\n", for Windows: test with "\r\n"
switch input {
case "Philip\r\n": fmt.Println("Welcome Philip!")
case "Chris\r\n": fmt.Println("Welcome Chris!")
case "Ivo\r\n": fmt.Println("Welcome Ivo!")
default: fmt.Printf("You are not welcome here! Goodbye!")
}
// version 2:
switch input {
case "Philip\r\n": fallthrough
case "Ivo\r\n": fallthrough
case "Chris\r\n": fmt.Printf("Welcome %s\n", input)
default: fmt.Printf("You are not welcome here! Goodbye!\n")
}
// version 3:
switch input {
case "Philip\r\n", "Ivo\r\n": fmt.Printf("Welcome %s\n", input)
default: fmt.Printf("You are not welcome here! Goodbye!\n")
}
}
```
注意Unix和Windows的行结束符是不同的
**练习**
**Exercise 12.1:** word_letter_count.go
Write a program which reads text from the keybord. When the user enters S in order to signal the end of the input, the program shows 3 numbers:
i) the number of characters including spaces (but excluding \r and \n)
ii) the number of words
iii) the number of lines
**Exercise 12.2:** calculator.go
Make a simple (reverse polish notation) calculator. This program accepts input from the user in the
form of integers (maximum 999999) and operators (+, -, *, /).
The input is like this: number1 ENTER number2 ENTER operator ENTER result is displayed.
The programs stops if the user inputs “q”. Use the package stack you developed in Ex. 11.3
## 链接
- [目录](directory.md)
- 上一节:[读写数据](12.0.md)
- 下一节:[文件读写](12.2.md)

View File

@@ -89,7 +89,7 @@
## 第三部分Go 高级编程 ## 第三部分Go 高级编程
- 第12章读写数据 - 第12章[读写数据](12.0.md)
- 第13章错误处理与测试 - 第13章错误处理与测试
- 第14章goroutine 与 channel - 第14章goroutine 与 channel
- 第15章网络、模版与网页应用 - 第15章网络、模版与网页应用

View File

@@ -0,0 +1,14 @@
package main
import (
"fmt"
"./pack1/pack1"
)
func main() {
var test1 string
test1 = pack1.ReturnStr()
fmt.Printf("ReturnStr from package1: %s\n", test1)
fmt.Printf(Integer from package1: %d\n, pack1.Pack1Int)
// fmt.Printf(“Float from package1: %f\n”, pack1.pack1Float)
}