Go学习笔记

https://books.studygolang.com/gopl-zh

入门

查找重复的行

修改dup2,出现重复的行时打印文件名称。

//dup2
func main() {
    counts := make(map[string]int)
    files := os.Args[1:]
    if len(files) == 0 {
        countlines(os.Stdin, counts)
    } else {
        for _, arg := range files {
            f, err := os.Open(arg)
            if err != nil {
                fmt.Fprintf(os.Stderr, "dup2:%v", err)
            }
            countlines(f, counts)
            f.Close()
        }
    }
}
func countlines(f *os.File, counts map[string]int) {
    input := bufio.NewScanner(f)
    for input.Scan() {
        counts[input.Text()]++
    }
    for line, n := range counts {
        if n > 1 {
            fmt.Println(f.Name())
            fmt.Printf("%d\t%s\n", n, line)
        }
    }
}

获取url

  • 函数调用io.Copy(dst, src)会从src中读取内容,并将读到的结果写入到dst中,使用这个函数替代掉例子中的ioutil.ReadAll来拷贝响应结构体到os.Stdout,避免申请一个缓冲区(例子中的b)来存储。记得处理io.Copy返回结果中的错误。
package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
)

func main() {
    for _, url := range os.Args[1:] {
        resp, err := http.Get(url)
        if err != nil {
            fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
            os.Exit(1)
        }
        _, err = io.Copy(os.Stdout, resp.Body)
        resp.Body.Close()
        if err != nil {
            fmt.Fprintf(os.Stderr,"fetch: reading: %s: %v\n",url,err)
        }
    }
}
  • 修改fetch这个范例,如果输入的url参数没有 http:// 前缀的话,为这个url加上该前缀。你可能会用到strings.HasPrefix这个函数。
package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "strings"
)

func main() {
    for _, url := range os.Args[1:] {
        var resp *http.Response
        var err error
        if strings.HasPrefix(url, "http://") {
            resp, err = http.Get(url)
        } else {
            resp, err = http.Get("http://" + url)
        }
        if err != nil {
            fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
            os.Exit(1)
        }
        _, err = io.Copy(os.Stdout, resp.Body)
        resp.Body.Close()
        if err != nil {
            fmt.Fprintf(os.Stderr, "fetch: reading: %s: %v\n", url, err)
        }
    }
}
  • 修改fetch打印出HTTP协议的状态码,可以从resp.Status变量得到该状态码。
fmt.Printf(resp.Status)

程序结构

变量

简短声明语句:=左边的变量不一定全是刚刚声明的,对于已经声明过的变量将执行赋值语句

in, err := os.Open(infile)
...
out, err := os.Create(outfile)

但是左边至少要有一个未声明过的变量。

f, err := os.Open(infile)
...
f, err := os.Create(outfile) //no new variables on left side of :=

包和文件

  • 向tempconv包添加类型、常量和函数用来处理Kelvin绝对温度的转换,Kelvin 绝对零度是−273.15°C,Kelvin绝对温度1K和摄氏度1°C的单位间隔是一样的。
//tempconv.go
package tempconv

import "fmt"

type Celsius float64
type Fahrenheit float64
type Kelvin float64

const (
    AbsoluteZeroC Celsius = -273.15
    FreezingC     Celsius = 0
    BoilingC      Celsius = 100
    AbsoluteZeroK Kelvin  = 0
    FreezingK     Kelvin  = 273.15
    BoilingK      Kelvin  = 373.15
)

func (c Celsius) String() string {
    return fmt.Sprintf("%g℃", c)
}

func (f Fahrenheit) String() string {
    return fmt.Sprintf("%g℉", f)
}

func (k Kelvin) String() string {
    return fmt.Sprintf("%g°K", k)
}

//conv.go
package tempconv

func CToF(c Celsius) Fahrenheit {
    return Fahrenheit(c*9/5 + 32)
}
func FToC(f Fahrenheit) Celsius {
    return Celsius((f - 32) * 5 / 9)
}

func CToK(c Celsius) Kelvin {
    return Kelvin(c + 273.15)
}

func KToC(k Kelvin) Celsius {
    return Celsius(k - 273.15)
}

包的初始化

  • 重写PopCount函数,用一个循环代替单一的表达式。
func PopCount(x uint64) int {
    var result byte
    var i unit64
    for i = 0; i < 8; i++ {
        result += pc[byte(x>>(i*uint64(8)))]
    }
    return int(result)
}
  • 用移位算法重写PopCount函数,每次测试最右边的1bit,然后统计总数。
func PopCount1(x uint64) int {
    num := 0
    for i := 0; x != 0; x >>= 1 {
        if x&1 == 1 {
            i++
        }
        num = i
    }
    return num
}
  • 表达式x&(x-1)用于将x的最低的一个非零的bit位清零。使用这个算法重写PopCount函数
func PopCount2(x uint64) int {
    num := 0
    for x != 0 {
        x = x & (x - 1)
        num++
    }
    return num
}

基础数据类型

字符串

  • basename
package main

import (
    "fmt"
    "os"
    "strings"
)
// basename removes directory components and a .suffix.
// e.g., a => a, a.go => a, a/b/c.go => c, a/b.c.go => b.c
// basename1
func basename1(s string) string {
    for i := len(s) - 1; i >= 0; i-- {
        if s[i] == '/' {
            s = s[i+1:]
            break
        }
    }
    for i := len(s) - 1; i >= 0; i-- {
        if s[i] == '.' {
            s = s[:i]
            break
        }
    }
    return s
}
// basename2
func basename2(s string) string {
    slash := strings.LastIndex(s, "/")
    s = s[slash+1:]
    if dot := strings.LastIndex(s, "."); dot >= 0 {
        s = s[:dot]
    }
    return s
}
func main() {
    for _, arg := range os.Args[1:] {
        _, _ = fmt.Println(basename2(arg))
    }
}
  • 编写一个非递归版本的comma函数,使用bytes.Buffer代替字符串链接操作。
// comma2
func comma2(s string) string {
    var buf bytes.Buffer
    n := len(s)
    if n%3 != 0 {
        _, _ = fmt.Fprintf(&buf, "%s", s[:n%3])
        buf.WriteString(",")
    }
    for i := n % 3; i+3 <= n; i += 3 {
        if i+3 == n {
            _, _ = fmt.Fprintf(&buf, "%s", s[i:])
            break
        }
        _, _ = fmt.Fprintf(&buf, "%s", s[i:i+3])
        buf.WriteString(",")
    }
    return buf.String()
}
  • 完善comma函数,以支持浮点数处理和一个可选的正负号的处理。
// comma3
func comma3(s string) string {
    var buf bytes.Buffer
    var integer string
    var decimal string
    var negative = strings.HasPrefix(s, "-")
    for i := len(s) - 1; i >= 0; i-- {
        if s[i] == '.' {
            integer = s[:i]
            decimal = s[i+1:]
            break
        } else {
            integer = s
        }
    }
    // integer
    if negative {
        integer = integer[1:]
        buf.WriteString("-")
    }
    n := len(integer)
    if n%3 != 0 {
        _, _ = fmt.Fprintf(&buf, "%s", integer[:n%3])
        buf.WriteString(",")
    }
    for i := n % 3; i+3 <= n; i += 3 {
        if i+3 == n {
            _, _ = fmt.Fprintf(&buf, "%s", integer[i:])
            break
        }
        _, _ = fmt.Fprintf(&buf, "%s", integer[i:i+3])
        buf.WriteString(",")
    }
    //decimal
    if decimal != "" {
        buf.WriteString(".")
    }
    m := len(decimal)
    for i := 0; i < m; i += 3 {
        if i+3 >= m {
            _, _ = fmt.Fprintf(&buf, "%s", decimal[i:])
            break
        }
        _, _ = fmt.Fprintf(&buf, "%s", decimal[i:i+3])
        buf.WriteString(",")
    }
    return buf.String()
}