1.1. Hello, World
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
}
Go是一门编译型语言,Go语言的工具链将源代码及其依赖转换成计算机的机器指令(译注:静态编译)。
前面是先要import package来调用基础包,然后在main function中可以调用library中的函数。
GO语言的强大之处在于它的基础包非常多,而且兼容性非常好,要好好的利用。
1.2. 命令行参数
os.Args变量是一个字符串(string)的切片(slice)‘
os包以跨平台的方式,提供了一些与操作系统交互的函数和变量。程序的命令行参数可从os包的Args变量获取;os包外部使用os.Args访问该变量。
package mainimport ( "fmt" "os" "strings")//!+func main() { fmt.Println(strings.Join(os.Args[1:], " "))}
这是书中的源代码,乍一看,挺简单的,s用来存放所有的命令行字符,sep用来加上空格,便于区分,os.Args会返回字符串切片类型。第一次进入循环时,sep还没有被赋值,此时sep为空,所以在第一次“s+=”操作时,前面没有空格,s加完后sep被赋值为空格,以后每次"s+="操作时都相当于加上一个“空格+命令行”,如此运行下来,s中便不会出现头尾有空格的情况,因此这里的sep赋值的位置和"s+="的位置是很考究的。
1.3. 查找重复的行
os.Open函数返回两个值。第一个值是被打开的文件(*os.File),其后被Scanner读取。
os.Open返回的第二个值是内置error类型的值。如果err等于内置值nil(译注:相当于其它语言里的NULL),那么文件被成功打开。读取文件,直到文件结束,然后调用Close关闭该文件,并释放占用的所有资源。相反的话,如果err的值不是nil,说明打开文件时出错了。这种情况下,错误值描述了所遇到的问题。我们的错误处理非常简单,只是使用Fprintf与表示任意类型默认格式值的动词%v,向标准错误流打印一条信息,然后dup继续处理下一个文件;continue语句直接跳到for循环的下个迭代开始执行。
-
bufio包,它使处理输入和输出方便又高效。Scanner类型是该包最有用的特性之一,它读取输入并将其拆成行或单词;通常是处理行形式的输入最简单的方法。 -
格式化
%d 十进制整数 %x, %o, %b 十六进制,八进制,二进制整数。 %f, %g, %e 浮点数: 3.141593 3.141592653589793 3.141593e+00 %t 布尔:true或false %c 字符(rune) (Unicode码点) %s 字符串 %q 带双引号的字符串"abc"或带单引号的字符'c' %v 变量的自然形式(natural format) %T 变量的类型 %% 字面上的百分号标志(无操作数)ioutil.ReadFile(filename)函数返回一个字节切片(byte slice)
1.4. GIF动画
练习 1.6: 修改Lissajous程序,修改其调色板来生成更丰富的颜色,然后修改SetColorIndex的第三个参数,看看显示结果吧。
package mainimport ( "image" "image/color" "image/gif" "io" "log" "math" "math/rand" "net/http" "os" "time")//!-main// Packages not needed by version in book.//!+mainvar palette []color.Color// const (// whiteIndex = 0 // first color in palette// blackIndex = 1 // next color in palette// )func main() { //!-main // The sequence of images is deterministic unless we seed // the pseudo-random number generator using the current time. // Thanks to Randall McPherson for pointing out the omission. rand.Seed(time.Now().UTC().UnixNano()) for i := 0; i < 10; i++ { r := uint8(rand.Uint32() % 256) g := uint8(rand.Uint32() % 256) b := uint8(rand.Uint32() % 256) palette = append(palette, color.RGBA{r, g, b, 0xff}) } if len(os.Args) > 1 && os.Args[1] == "web" { //!+http handler := func(w http.ResponseWriter, r *http.Request) { lissajous(w) } http.HandleFunc("/", handler) //!-http log.Fatal(http.ListenAndServe("localhost:8000", nil)) return } //!+main lissajous(os.Stdout)}func lissajous(out io.Writer) { const ( cycles = 5 // number of complete x oscillator revolutions res = 0.001 // angular resolution size = 100 // image canvas covers [-size..+size] nframes = 64 // number of animation frames delay = 8 // delay between frames in 10ms units ) freq := rand.Float64() * 3.0 // relative frequency of y oscillator anim := gif.GIF{LoopCount: nframes} phase := 0.0 // phase difference for i := 0; i < nframes; i++ { rect := image.Rect(0, 0, 2*size+1, 2*size+1) img := image.NewPaletted(rect, palette) for t := 0.0; t < cycles*2*math.Pi; t += res { x := math.Sin(t) y := math.Sin(t*freq + phase) img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), (uint8)(i%10)) } phase += 0.1 anim.Delay = append(anim.Delay, delay) anim.Image = append(anim.Image, img) } gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors}//!-main
这里是修改了palette variable,把它赋成了一个切片,里面存了10种颜色,这样可以让每一个相邻的frame 得到跟之前的frame不同的颜色,让最后的输出变的颜色不一样了,并不是之前的单调的黑白颜色。
1.5. 获取URL
http.Get函数是创建HTTP请求的函数,如果获取过程没有出错,那么会在resp这个结构体中得到访问的请求结果。resp的Body字段包括一个可读的服务器响应流。ioutil.ReadAll函数从response中读取到全部内容;将其结果保存在变量b中。resp.Body.Close关闭resp的Body流,防止资源泄露,Printf函数会将结果b写出到标准输出流中。
1.6. 并发获取多个URL
我觉得这里的goroutine设计的非常的神奇,可以让程序去开始很多的轻量级的并发小程序。这样的开发使得并行的程序可以高质量的运行起来。
1.7. Web服务
Go语言的内置库使得写一个类似fetch的web服务器变得异常地简单。在本节中,我们会展示一个微型服务器,这个服务器的功能是返回当前用户正在访问的URL。比如用户访问的是 http://localhost:8000/hello ,那么响应是URL.Path = "hello"。
// Server1 is a minimal "echo" server.
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", handler) // each request calls handler
log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
// handler echoes the Path component of the request URL r.
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}
main函数将所有发送到/路径下的请求和handler函数关联起来,/开头的请求其实就是所有发送到当前站点上的请求,服务监听8000端口。发送到这个服务的“请求”是一个http.Request类型的对象,这个对象中包含了请求中的一系列相关字段,其中就包括我们需要的URL。当请求到达服务器时,这个请求会被传给handler函数来处理,这个函数会将/hello这个路径从请求的URL中解析出来,然后把其发送到响应中,这里我们用的是标准输出流的fmt.Fprintf。
handler := func(w http.ResponseWriter, r *http.Request) {
lissajous(w)
}
http.HandleFunc("/", handler)
在这些程序中,我们看到了很多不同的类型被输出到标准输出流中。比如前面的fetch程序,把HTTP的响应数据拷贝到了os.Stdout,lissajous程序里我们输出的是一个文件。fetchall程序则完全忽略到了HTTP的响应Body,只是计算了一下响应Body的大小,这个程序中把响应Body拷贝到了ioutil.Discard。在本节的web服务器程序中则是用fmt.Fprintf直接写到了http.ResponseWriter中。