读取用户的输入

从键盘和标准输入 os.Stdin 读取输入,最简单的办法是使用 fmt 包提供的 Scan 和 Sscan 开头的函数

  1. package main
  2. import "fmt"
  3. var (
  4. firstName, lastName, s string
  5. i int
  6. f float32
  7. input = "56.12 / 5212 / Go"
  8. format = "%f / %d / %s"
  9. )
  10. func main() {
  11. fmt.Println("Please enter your full name: ")
  12. fmt.Scanln(&firstName, &lastName)
  13. // fmt.Scanf("%s %s",&firstName, &lastName)
  14. fmt.Printf("Hi %s %s!\n", firstName, lastName)
  15. fmt.Sscanf(input, format, &f, &i, &s)
  16. fmt.Println("From the string we read: ", f, i, s)
  17. }

Scanln 扫描来自标准输入的文本,将空格分隔的值依次存放到后续的参数内,直到碰到换行。Scanf 与其类似,除了 Scanf 的第一个参数用作格式字符串,用来决定如何读取。Sscan 和以 Sscan 开头的函数则是从字符串读取,除此之外,与 Scanf 相同。

也可以使用bufio包提供的缓冲读取(buffered reader)来读取函数:

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "os"
  6. )
  7. // var inputReader *bufio.Reader
  8. // var input string
  9. // var err error
  10. func main() {
  11. inputReader := bufio.NewReader(os.Stdin)
  12. fmt.Println("Please enter some input: ")
  13. input, err := inputReader.ReadString('\n')
  14. if err == nil {
  15. fmt.Printf("The input was: %s\n", input)
  16. }
  17. }

inputReader是一个指向bufio.Reader的指针。inputReader = bufio.NewReader(os.Stdin)这行代码,将会创建一个读取器,并将其与标准输入绑定。

bufio.NewReader()构建函数的签名为:func NewReader(rd io.Reader) *Reader,该函数的实参可以是满足io.Reader接口的任意对象,函数返回一个新的带缓冲的io.Reader的对象,它将从指定读取器(如 os.Stdin)读取内容。

返回的读取器对象提供一个方法ReadString(delim byte),该方法从输入中读取内容,直到碰到delim指定的字符,然后将读取到的内容连同delim字符一起放到缓冲区。

ReadString返回读取到的字符串,如果碰到错误则返回nil。如果它一直读取到文件结束,则返回读取到的字符串和io.EOF。如果读取过程中没有碰到delim字符,将返回错误err != nil。

文件读写

文件使用指向os.File类型的指针来表示的,也叫做文件句柄。前面使用到过标准输入os.Stdin和标准输出os.Stdout,它们的类型都是*os.File。

fileinput.go:

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "os"
  7. )
  8. func main() {
  9. path, _ := os.Getwd()
  10. fmt.Println("Path: ", path)
  11. inputFile, inputError := os.Open("MaGedu-Go/local_code/io/input.txt")
  12. if inputError != nil {
  13. fmt.Printf("An error occurred on opening the inputfile\n" + "Dose the file exist?\n" + "Have you got access to it?\n")
  14. return
  15. }
  16. defer inputFile.Close()
  17. inputReader := bufio.NewReader(inputFile)
  18. for {
  19. inputString, readerError := inputReader.ReadString('\n')
  20. fmt.Printf("The input was: %s", inputString)
  21. if readerError == io.EOF {
  22. return
  23. }
  24. }
  25. }

变量inputFile是*os.File类型的。该类型是一个结构,表示一个打开文件的描述符(文件句柄)。使用os.Open打开文件,该函数的参数是文件名,类型为string,上边的示例是以只读模式打开文件。

如果文件不存在或者没有足够的权限打开,Open函数会返回一个错误。如果文件打开正常,我们就使用defer inputFile.Close()语句确保在程序退出前关闭该文件。然后使用bufio.NewReader来获的一个读取器变量。

注意: 在使用ReadString和ReadBytes方法的时候,不需要关心操作系统的类型,直接使用\n就可以了,另外,也可以使用ReadLine()方法来实现相同的功能。

一旦读取到文件末尾,变量readerError的值将变成非空(事实上,其值为常量io.EOF),会执行return语句从而退出循环。

1.将整个文件的内容读到一个字符串里:

可以使用io/ioutil包里的ioutil.ReadFile()方法,该方法第一个返回值的类型是[]byte,里面存放读取到的内容,第二个返回值是错误,如果没有错误发生,第二个返回值为nil。

read_write_file1.go:

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. )
  7. func main() {
  8. inputFile := "MaGedu-Go/local_code/io/input.txt"
  9. outputFile := "MaGedu-Go/local_code/io/output.txt"
  10. buf, err := ioutil.ReadFile(inputFile)
  11. if err != nil {
  12. fmt.Fprintf(os.Stderr, "File Error: %s\n", err)
  13. // panic(err.Error())
  14. }
  15. fmt.Printf("%s\n", string(buf))
  16. err = ioutil.WriteFile(outputFile, buf, 0644)
  17. if err != nil {
  18. panic(err.Error())
  19. }
  20. }

2.带缓冲的读取

在很多情况下,文件的内容是不安行划分的,或者干脆就是一个二进制文件。在这种情况下,ReadString()就无法使用了,可以使用bufio.Reader的Read(),它只接收一个参数:

  1. buf := make([]byte, 1024)
  2. ...
  3. n, err := inputReader.Read(buf)
  4. if (n == 0) { break }

3.按列读取文件中的数据

如果数据是按列排列并用空格分隔的,可以使用fmt包提供的以FScan开头的一系列函数来读取他们。以下示例,将3列的数据分别读入变量v1、v2和v3内,然后分别添加到切片的尾部。

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. )
  6. func main() {
  7. file, err := os.Open("MaGedu-Go/local_code/io/input.txt")
  8. if err != nil {
  9. panic(err)
  10. }
  11. defer file.Close()
  12. var col1, col2, col3 []string
  13. for {
  14. var v1, v2, v3 string
  15. _, err := fmt.Fscanln(file, &v1, &v2, &v3)
  16. // scans until newline
  17. if err != nil {
  18. break
  19. }
  20. col1 = append(col1, v1)
  21. col2 = append(col2, v2)
  22. col3 = append(col3, v3)
  23. }
  24. fmt.Println(col1)
  25. fmt.Println(col2)
  26. fmt.Println(col3)
  27. }

注意: path包里包含一个子包叫filepath,其提供了跨平台的函数,用于处理文件名和路径。例如Base()函数用于获得路径中的最后一个元素(不包含后面的分隔符):

  1. import "path/filepath"
  2. filename := filepath.Base(path)

compress包:读取压缩文件

支持的压缩文件格式为:bzip2、flate、gzip、lzw和zlib

gzipped.go:

  1. package main
  2. import (
  3. "bufio"
  4. "compress/gzip"
  5. "fmt"
  6. "os"
  7. )
  8. func main() {
  9. fName := "test.gz"
  10. var r *bufio.Reader
  11. fi, err := os.Open(fName)
  12. if err != nil {
  13. fmt.Fprintf(os.Stderr, "%v, Can't open %s: error: %s\n", os.Args[0], fName, err)
  14. os.Exit(1)
  15. }
  16. defer fi.Close()
  17. fz, err := gzip.NewReader(fi)
  18. if err != nil {
  19. r = bufio.NewReader(fi)
  20. } else {
  21. r = bufio.NewReader(fz)
  22. }
  23. for {
  24. line, err := r.ReadString('\n')
  25. if err != nil {
  26. fmt.Println("Done reading file")
  27. os.Exit(0)
  28. }
  29. fmt.Println(line)
  30. }
  31. }

写文件

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "os"
  6. )
  7. func main() {
  8. outputFile, outputError := os.OpenFile("MaGedu-Go/local_code/io/output.txt", os.O_WRONLY|os.O_CREATE, 0666)
  9. if outputError != nil {
  10. fmt.Printf("An error\n")
  11. return
  12. }
  13. defer outputFile.Close()
  14. outputWriter := bufio.NewWriter(outputFile)
  15. outputString := "hello world\n"
  16. for i := 0; i < 10; i++ {
  17. outputWriter.WriteString(outputString)
  18. }
  19. outputWriter.Flush()
  20. }

OpenFile函数有三个参数:文件名、一个或多个标志(使用逻辑运算符”|”连接),使用的文件权限。

  • os.O_RDONLY: 只读
  • os.O_WRONLY: 只写
  • os.O_CREATE: 创建,如果指定文件不存在,就创建该文件
  • os.O_TRUNC: 截断,如果指定文件已存在,就将该文件的长度截为0

在读文件的时候,文件的权限是被忽略的,所以在使用OpenFile时传入的第三个参数可以用0。而在写文件时,不管是Unix还是Windows,都需要使用0666。

然后,创建一个写入器(缓冲区)对象:outputWriter := bufio.NewWriter(outputFile),将字符串写入缓冲区,outputWriter.WriteString(outputString),缓冲区的内容紧接着被完全写入文件:outputWriter.Flush()

如果写入的东西很简单,可以使用fmt.Fprintf(outputFile, “Some test data.\n”)直接将内容写入文件。fmt包里的F开头的Print函数可以直接写入任何io.Writer,包括文件。

示例:不适用fmt.Fprintf函数
filewrite.go:

  1. package main
  2. import "os"
  3. func main() {
  4. os.Stdout.WriteString("hello, world\n")
  5. f, _ := os.OpenFile("test", os.O_CREATE|os.O_WRONLY, 0666)
  6. defer f.Close()
  7. f.WriteString("hello world\n")
  8. }

文件拷贝

  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. )
  7. func CopyFile(dstName, srcName string) (writen int64, err error) {
  8. src, err := os.Open(srcName)
  9. if err != nil {
  10. return
  11. }
  12. defer src.Close()
  13. dst, err := os.Create(dstName)
  14. if err != nil {
  15. return
  16. }
  17. defer dst.Close()
  18. return io.Copy(dst, src)
  19. }
  20. func main() {
  21. CopyFile("MaGedu-Go/local_code/io/des.txt", "MaGedu-Go/local_code/io/src.txt")
  22. fmt.Println("Copy done!")
  23. }

从命令行读取参数

os包

os包中有一个string类型的切片变量os.Args,用来处理一些基本的命令行参数,它在程序启动后读取命令行输入的参数。

os_args.go:

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "strings"
  6. )
  7. func main() {
  8. who := "Alice"
  9. if len(os.Args) > 1 {
  10. who += strings.Join(os.Args[1:], " ")
  11. }
  12. fmt.Println("Good Morning", who)
  13. }

flag包

flag包有一个扩展功能用来解析命令行选项。但是通常被用来替换基本常量。

在flag包中有一个Flag被定义成一个含有如下字段的结构体:

  1. type Flag struct {
  2. Name string
  3. Usage string
  4. Value Value
  5. DefValue string
  6. }

echo.go:

  1. package main
  2. import (
  3. "flag"
  4. "os"
  5. )
  6. var NewLine = flag.Bool("n", false, "print newline") // echo -n flag, of type *bool
  7. const (
  8. Space = " "
  9. Newline = "\n"
  10. )
  11. func main() {
  12. flag.PrintDefaults()
  13. // Scans the arg list and sets up flags
  14. flag.Parse()
  15. s := ""
  16. for i := 0; i < flag.NArg(); i++ {
  17. if i > 0 {
  18. s += " "
  19. if *NewLine {
  20. s += Newline
  21. }
  22. }
  23. s += flag.Arg(i)
  24. }
  25. os.Stdout.WriteString(s)
  26. }

flag.Parse()扫描参数列表(或常量列表)并设置flag,flag.Arg(i)表示第i个参数。Parse()之后flag.Arg(i)全部可用,flag.Arg(0)就是第一个真实的flag,而不是像os.Args(0)放置程序的名字。

flag.Narg()返回参数的数量。解析后flag或常量就可用了。flag.Bool()定义了一个默认值是false的flag:当在命令行出现了第一个参数(这里是’n’),flag被设置成true(NewLine是_bool类型)。flag被解引用到 _NewLine,所以当值是true时将添加一个Newline

flag.PrintDefaults()打印flag的使用帮助信息,本例中打印的是:

  1. -n=false: print newline

flag.VisitAll(fn func(*Flag))是另一个有用的功能:按照字典顺序遍历flag,并且对每个标签调用fn

对于flag.Bool可以设置布尔型flag来测试你的代码,例如定义一个flag processedFlag:
var processedFlag = flag.Bool("proc", false, "nothing processed yet")
在后面用如下代码来测试:

  1. if *processedFlag { // fount flag -proc
  2. r = process()
  3. }

用buffer读取文件

在下面的例子中,结合使用了缓冲读取文件和命令行 flag 解析这两项技术。如果不加参数,那么你输入什么屏幕就打印什么。

参数被认为是文件名,如果文件存在的话就打印文件内容到屏幕。命令行执行 cat test 测试输出。

cat.go:

  1. package main
  2. import (
  3. "bufio"
  4. "flag"
  5. "fmt"
  6. "io"
  7. "os"
  8. )
  9. func cat(r *bufio.Reader) {
  10. for {
  11. buf, err := r.ReadBytes('\n')
  12. fmt.Fprintf(os.Stdout, "%s", buf)
  13. if err == io.EOF {
  14. break
  15. }
  16. }
  17. return
  18. }
  19. func main() {
  20. flag.Parse()
  21. if flag.NArg() == 0 {
  22. cat(bufio.NewReader(os.Stdin))
  23. }
  24. for i := 0; i < flag.NArg(); i++ {
  25. f, err := os.Open(flag.Arg(i))
  26. if err != nil {
  27. fmt.Fprintf(os.Stderr, "%s: error reading from %s: %s\n", os.Args[0], flag.Arg(i), err.Error())
  28. continue
  29. }
  30. cat(bufio.NewReader(f))
  31. f.Close()
  32. }
  33. }

用切片读写文件

切片提供了 Go 中处理 I/O 缓冲的标准方式

cat2.go:

  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "os"
  6. )
  7. func cat(f *os.File) {
  8. const NBUF = 512
  9. var buf [NBUF]byte
  10. for {
  11. switch nr, err := f.Read(buf[:]); true {
  12. case nr < 0:
  13. fmt.Fprintf(os.Stderr, "cat: error reading: %s\n", err.Error())
  14. os.Exit(1)
  15. case nr == 0: //EOF
  16. return
  17. case nr > 0:
  18. if nw, ew := os.Stdout.Write(buf[0:nr]); nw != nr {
  19. fmt.Fprintf(os.Stderr, "cat: error writing: %s\n", ew.Error())
  20. }
  21. }
  22. }
  23. }
  24. func main() {
  25. flag.Parse()
  26. if flag.NArg() == 0 {
  27. cat(os.Stdin)
  28. }
  29. for i := 0; i < flag.NArg(); i++ {
  30. f, err := os.Open(flag.Arg(i))
  31. if err != nil {
  32. fmt.Fprintf(os.Stderr, "%s: error reading from %s: %s\n", os.Args[0], flag.Arg(i), err.Error())
  33. os.Exit(1)
  34. }
  35. cat(f)
  36. f.Close()
  37. }
  38. }

使用接口的实际例子:fmt.Fprintf

io_interfaces.go:

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "os"
  6. )
  7. func main() {
  8. // unbuffered
  9. fmt.Fprintf(os.Stdout, "%s\n", "hello world! - unbuffered")
  10. // buffered: os.Stdout implements io.Writer
  11. buf := bufio.NewWriter(os.Stdout)
  12. fmt.Fprintf(buf, "%s\n", "hello world! - buffered")
  13. buf.Flush()
  14. }

fmt.Fprintf(),func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)

不是写入一个文件,而是写入一个io.Writer接口类型的变量,下面是Writer接口在io包中的定义:

  1. type Writer interface {
  2. Write(p []byte) (n int, err error)
  3. }

fmt.Fprintf() 依据指定的格式向第一个参数内写入字符串,第一个参数必须实现了 io.Writer 接口。Fprintf() 能够写入任何类型,只要其实现了 Write 方法,包括 os.Stdout,文件(例如 os.File),管道,网络连接,通道等等,同样的也可以使用 bufio 包中缓冲写入。bufio 包中定义了 type Writer struct{…} 。

bufio.Writer 实现了 Write 方法:

  1. func (b *Writer) Write(p []byte) (nn int, err error)

它还有一个工厂函数:传给它一个 io.Writer 类型的参数,它会返回一个带缓冲的 bufio.Writer 类型的 io.Writer :

  1. func NewWriter(wr io.Writer) (b *Writer)

适合任何形式的缓冲写入。

在缓冲写入的最后千万不要忘了使用 Flush(),否则最后的输出不会被写入。

JSON数据格式

通过把数据转换成纯文本,使用命名的字段来标注,让其具有可读性。这样的数据格式可以通过网络传输,而且是与平台无关的,任何类型的应用都能够读取和输出,不与操作系统和编程语言的类型相关。

术语说明:

  • 数据结构 —> 指定格式 = 序列化或编码(传输之前)
  • 指定格式 —> 数据结构 = 反序列化或解码(传输之后)

序列化是在内存中把数据转换成指定格式(data -> string),反之亦然(string -> data)。

编码也是一样的,只是输出一个数据流(实现了 io.Writer 接口);解码是从一个数据流(实现了 io.Reader)输出到一个数据结构。

json.go:

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log"
  6. "os"
  7. )
  8. type Address struct {
  9. Type string
  10. City string
  11. Country string
  12. }
  13. type VCard struct {
  14. FirstName string
  15. LastName string
  16. Addresses []*Address
  17. Remark string
  18. }
  19. func main() {
  20. pa := &Address{"private", "Aartselaar", "Belgiunm"}
  21. wa := &Address{"work", "Boom", "Belgium"}
  22. vc := VCard{"Jan", "Kersschot", []*Address{pa, wa}, "none"}
  23. js, _ := json.Marshal(vc)
  24. fmt.Printf("JSON format: %s\n", js)
  25. file, _ := os.OpenFile("vcard.json", os.O_CREATE|os.O_WRONLY, 0666)
  26. defer file.Close()
  27. enc := json.NewEncoder(file)
  28. err := enc.Encode(vc)
  29. if err != nil {
  30. fmt.Println(err)
  31. log.Println("Error in encoding json")
  32. }
  33. }

出于安全考虑,在web应用中最好使用json.MarshalforHTML(),其对数据执行HTML转码,所以文本可以被安全地嵌在HTMLscript标签中。

不是所有的数据都可以编码为JSON类型,只有验证通过的数据结构才能被编码:

  • JSON对象只支持字符串类型的key;要编码一个Go map类型,map必须是map[string]T(T是json包中支持的任何类型)
  • Channel,复杂类型和函数类型不能被编码
  • 不支持循环数据结构;它将引起序列化进入一个无限循环
  • 指针可以被编码,实际上是对指针指向的值进行编码(或者指针是nil)

反序列化

json.Unmarshal()的函数签名是func Unmarshal(data []byte, v interface{}) error 把JSON解码为数据结构。

上边示例中对vc编码后的数据为js,对其解码时,首先创建结构VCard用来保存解码的数据:var v VCard并调用json.Unmarshal(js,&v),解析[]byte中的JSON数据并将结果存入指针&v指向的值

反射能够让JSON字段去尝试匹配目标结构字段;但是只有真正匹配上的字段才会填充数据。字段没有匹配不会报错,而是直接忽略掉。

  1. package main
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "strings"
  7. )
  8. type Person struct {
  9. Name string `json: "name"`
  10. Age int `json: "age"`
  11. }
  12. func main() {
  13. // 1.编码
  14. person1 := Person{"张三", 24}
  15. bytes1, err := json.Marshal(&person1)
  16. if err == nil {
  17. // 返回是的字节数组 []byte
  18. fmt.Println("json.Marshal 编码结果:", string(bytes1))
  19. }
  20. // 2.解码
  21. str := `{"name":"李四", "age":25}`
  22. // json.Unmarshal 需要字节数组参数,需要把字符串转为 []byte 类型
  23. bytes2 := []byte(str)
  24. var person2 Person
  25. if json.Unmarshal(bytes2, &person2) == nil {
  26. fmt.Println("json.Unmarshal 解码结果:", person2.Name, person2.Age)
  27. }
  28. // 3.使用json.NewEncoder 编码
  29. person3 := Person{"王五", 26}
  30. // 编码结果暂存到buffer
  31. bytes3 := new(bytes.Buffer)
  32. _ = json.NewEncoder(bytes3).Encode(person3)
  33. if err == nil {
  34. fmt.Print("json.NewEncoder 编码结果:", string(bytes3.Bytes()))
  35. }
  36. // 4.使用json.NewDecoder 解码
  37. str4 := `{"name":"赵六", "age": 27}`
  38. var person4 Person
  39. err = json.NewDecoder(strings.NewReader(str4)).Decode(&person4)
  40. if err == nil {
  41. fmt.Println("json.NewDecoder 解码结果:", person4.Name, person4.Age)
  42. }
  43. }

解码任意的数据:

json包使用map[string]interface()和[]interface()存储任意的JSON对象和数组;其可以被反序列化为任何的JSON blob存储到接口值中。

b := []byte({"Name": "Wednesday", "Age": 6, "Parents": ["Gomez", "Morticia"]})

不用理解这个数据的结构,可以直接使用Unmarshal把这个数据编码并保存在接口值中:

  1. var f interface{}
  2. err := json.Unmarshal(b, &f)

f指向的值是一个map,key是一个字符串,value是自身存储作为空接口类型的值:

  1. map[string]interface{} {
  2. "Name": "Wednesday",
  3. "Age": 6,
  4. "Parenets": []interface{} {
  5. "Gomez",
  6. "Morticia",
  7. },
  8. }

要访问这个数据,可以使用类型断言:m := f.(map[string]interface{})

可以通过for range语法和type switch来访问其实际类型:

  1. for k, v := range m {
  2. switch vv := v.(type) {
  3. case string:
  4. fmt.Println(k, "is string", vv)
  5. case int:
  6. fmt.Println(k, "is int", vv)
  7. case []interface{}:
  8. fmt.Println(k, "is an array:")
  9. for i, u := range vv {
  10. fmt.Println(i, u)
  11. }
  12. default:
  13. fmt.Println(k, "is of a type")
  14. }
  15. }

解码数据到结构体

如果事先知道JSON数据,可以定义一个适当的结构并对JSON数据反序列化:

  1. type FamilyMember struct {
  2. Name string
  3. Age int
  4. Parents []string
  5. }
  6. var m FamilyMember
  7. err := json.Unmarshal(b,&m)

编码和解码流

json包提供Decoder和Encoder类型来支持常用JSON数据流读写。NewDecoder和NewEncoder函数分别封装了io.Reader和io.Writer接口。

  1. func NewDecoder(r io.Reader) *Decoder
  2. func NewEncoder(w io.Writer) *Encoder

要想把JSON直接写入文件,可以使用json.NewEncoder初始化文件(或者任何实现 io.Writer 的类型),并调用 Encode();反过来与其对应的是使用 json.NewDecoder 和 Decode() 函数:

  1. func NewDecoder(r io.Reader) *Decoder
  2. func (dec *Decoder) Decode(v interface{}) error

接口是如何对实现进行抽象的:数据结构可以是任何类型,只要其实现了某种接口,目标或源数据要能够被编码就必须实现 io.Writer 或 io.Reader 接口。由于 Go 语言中到处都实现了 Reader 和 Writer,因此 Encoder 和 Decoder 可被应用的场景非常广泛,例如读取或写入 HTTP 连接、websockets 或文件。

XML数据格式

  1. // xml.go
  2. package main
  3. import (
  4. "encoding/xml"
  5. "fmt"
  6. "strings"
  7. )
  8. var t, token xml.Token
  9. var err error
  10. func main() {
  11. input := "<Person><FirstName>Laura</FirstName><LastName>Lynn</LastName></Person>"
  12. inputReader := strings.NewReader(input)
  13. p := xml.NewDecoder(inputReader)
  14. for t, err = p.Token(); err == nil; t, err = p.Token() {
  15. switch token := t.(type) {
  16. case xml.StartElement:
  17. name := token.Name.Local
  18. fmt.Printf("Token name: %s\n", name)
  19. for _, attr := range token.Attr {
  20. attrName := attr.Name.Local
  21. attrValue := attr.Value
  22. fmt.Printf("An attribute is: %s %s\n", attrName, attrValue)
  23. // ...
  24. }
  25. case xml.EndElement:
  26. fmt.Println("End of token")
  27. case xml.CharData:
  28. content := string([]byte(token))
  29. fmt.Printf("This is the content: %v\n", content)
  30. // ...
  31. default:
  32. // ...
  33. }
  34. }
  35. }

加密

通过网络传输的数据必须加密,以防止被 hacker(黑客)读取或篡改,并且保证发出的数据和收到的数据检验和一致。

  • hash 包:实现了 adler32、crc32、crc64 和 fnv 校验。
  • crypto 包:实现了其它的 hash 算法,比如 md4、md5、sha1 等。以及完整地实现了 aes、blowfish、rc4、rsa、xtea 等加密算法。

hash_sha1.go:

  1. package main
  2. import (
  3. "crypto/sha1"
  4. "fmt"
  5. "io"
  6. "log"
  7. )
  8. func main() {
  9. hasher := sha1.New()
  10. io.WriteString(hasher, "test")
  11. b := []byte{}
  12. fmt.Printf("Result: %x\n", hasher.Sum(b))
  13. fmt.Printf("Result: %d\n", hasher.Sum(b))
  14. hasher.Reset()
  15. data := []byte("We shall overcome!")
  16. n, err := hasher.Write(data)
  17. if n != len(data) || err != nil {
  18. log.Printf("Hash write error: %v / %v", n, err)
  19. }
  20. checksum := hasher.Sum(b)
  21. fmt.Printf("Result: %x\n", checksum)
  22. }