1.读取用户的输入

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

  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 ful 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 strin we read: ", f, i, s)
  17. }

可以使用 bufio 包提供的缓冲读取(buffered reader)来读取数据
Unix和Linux的行结束符是 \n,而Windows的行结束符是 \r\n。

  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. }

2.文件读写

读取文件方式汇总

读:
bufio.NewReader(inputFile).ReadString('\n')
buf, err := ioutil.ReadFile("in.txt")
bufio.Reader.Read(buf)
写:
os.OpenFile("out.txt", os.O_WRONLY|os.O_CREATE, 0666)
bufio.NewWriter(outputFile).WriteString(outputStr)
fmt.Fprintf(outputFile, “Some test data.\n”)
os.OpenFile("test", os.O_CREATE|os.O_WRONLY, 0).WriteString(outputStr)
ioutil.WriteFile(outputFile, buf, 0644)

读文件

文件使用指向 os.File 类型的指针来表示的,也叫做文件句柄

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "os"
  7. )
  8. func main() {
  9. inputFile, inputError := os.Open("fileinput/1.txt")
  10. if inputError != nil {
  11. fmt.Println("file error", inputError)
  12. return
  13. }
  14. defer inputFile.Close()
  15. inputReader := bufio.NewReader(inputFile)
  16. for {
  17. inputString, readerError := inputReader.ReadString('\n')
  18. fmt.Printf("the input was: %s", inputString)
  19. if readerError == io.EOF {
  20. return
  21. }
  22. }
  23. }

将整个文件的内容读到一个字符串里:
使用 io/ioutil 包里的 ioutil.ReadFile() 方法,该方法第一个返回值的类型是 []byte,里面存放读取到的内容,第二个返回值是错误,如果没有错误发生,第二个返回值为 nil。类似的,函数 WriteFile() 可以将 []byte 的值写入文件。

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

带缓冲的读取

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

按列读取文件中的数据

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. )
  6. func main() {
  7. file, err := os.Open("products2.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. }

练习:
文件 products.txt 的内容如下:

  1. "The ABC of Go";25.5;1500
  2. "Functional Programming with Go";56;280
  3. "Go for It";45.9;356
  4. "The Go Way";55;500

每行的第一个字段为 title,第二个字段为 price,第三个字段为 quantity。内容的格式基本与 示例 12.3c 的相同,除了分隔符改成了分号。请读取出文件的内容,创建一个结构用于存取一行的数据,然后使用结构的切片,并把数据打印出来。

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "os"
  7. "strconv"
  8. "strings"
  9. )
  10. type product struct {
  11. title string
  12. price float64
  13. quantity int
  14. }
  15. func main() {
  16. file, err := os.Open("from.txt")
  17. if err != nil {
  18. fmt.Println(err)
  19. }
  20. defer file.Close()
  21. var products []product
  22. reader := bufio.NewReader(file)
  23. for {
  24. readString, readerErr := reader.ReadString('\n')
  25. readString = strings.TrimSpace(readString)
  26. split := strings.Split(readString, ";")
  27. fmt.Println(readString)
  28. p, _ := strconv.ParseFloat(split[1], 32)
  29. fmt.Println("p ", p)
  30. q, _ := strconv.Atoi(split[2])
  31. fmt.Println("q ", q)
  32. pr := product{split[0], p, q}
  33. products = append(products, pr)
  34. if readerErr == io.EOF {
  35. break
  36. }
  37. }
  38. fmt.Println(products)
  39. }

compress包:读取压缩文件

有需要再学

写文件

我们通常会用到以下标志:

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

1)除了文件句柄,我们还需要 bufio 的 Writer。我们以只写模式打开文件 output.dat,如果文件不存在则自动创建
缓冲区的内容紧接着被完全写入文件:outputWriter.Flush()

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "os"
  6. )
  7. func main() {
  8. outputFile, outputError := os.OpenFile("out.txt", os.O_WRONLY|os.O_CREATE, 0666)
  9. if outputError != nil {
  10. fmt.Println(outputError)
  11. return
  12. }
  13. defer outputFile.Close()
  14. outputWriter := bufio.NewWriter(outputFile)
  15. outputStr := "你好 world\n"
  16. for i := 0; i < 10; i++ {
  17. outputWriter.WriteString(outputStr)
  18. }
  19. outputWriter.Flush()
  20. }

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

  1. f, _ := os.OpenFile("test", os.O_CREATE|os.O_WRONLY, 0)
  2. defer f.Close()
  3. f.WriteString("hello, world in a file\n")


4)ioutil.WriteFile(outputFile, buf, 0644)

3.文件拷贝

如何拷贝一个文件到另一个文件?最简单的方式就是使用 io 包:io.Copy

  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. )
  7. func main() {
  8. file, err := CopyFile("out.txt", "from.txt")
  9. if err != nil {
  10. fmt.Println(err)
  11. }
  12. fmt.Println(file)
  13. }
  14. func CopyFile(dstName, sourceName string) (written int64, err error) {
  15. source, err := os.Open(sourceName)
  16. if err != nil {
  17. return
  18. }
  19. defer source.Close()
  20. dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
  21. if err != nil {
  22. return
  23. }
  24. defer dst.Close()
  25. return io.Copy(dst, source)
  26. }

4.从命令行读取参数

这个命令行参数会放置在切片 os.Args[] 中(以空格分隔),从索引1开始(os.Args[0] 放的是程序本身的名字,在本例中是 os_args)。函数 strings.Join 以空格为间隔连接这些参数。

  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.Parse() 扫描参数列表(或者常量列表)并设置 flag, flag.Arg(i) 表示第i个参数。
flag.Parse() 扫描参数列表(或者常量列表)并设置 flag, flag.Arg(i) 表示第i个参数。
flag.Bool() 定义了一个默认值是 false 的 flag:当在命令行出现了第一个参数(这里是 “n”),flag 被设置成 true(NewLine 是 bool 类型)。flag 被解引用到 NewLine,所以当值是 true 时将添加一个 newline(”\n”)。
flag.PrintDefaults() 打印 flag 的使用帮助信息。
flag.VisitAll(fn func(*Flag)) 是另一个有用的功能:按照字典顺序遍历 flag,并且对每个标签调用 fn。

  1. package main
  2. import (
  3. "flag"
  4. "os"
  5. )
  6. var NewLine = flag.Bool("n", false, "print newLine")
  7. const (
  8. Space = " "
  9. Newline = "\n"
  10. )
  11. func main() {
  12. flag.PrintDefaults()
  13. flag.Parse() //扫描参数列表并且建立flags
  14. var s string = ""
  15. for i := 0; i < flag.NArg(); i++ {
  16. if i > 0 {
  17. s += " "
  18. if *NewLine {
  19. s += Newline
  20. }
  21. }
  22. s += flag.Arg(i)
  23. }
  24. os.Stdout.WriteString(s)
  25. }

go run .\echo.go -n 1 2 3
-n print newLine
1
2
3
go run .\echo.go 1 2 3
-n print newLine
1 2 3

4.用buffer读文件

参数被认为是文件名,如果文件存在的话就打印文件内容到屏幕。

  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. if err == io.EOF {
  13. break
  14. }
  15. fmt.Fprintf(os.Stdout, "%s", buf)
  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. }
  32. }

image.png

5.用切片读取文件

  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.Fprint(os.Stderr, "cat: error reading: %s\n", err.Error())
  14. os.Exit(1)
  15. case nr == 0:
  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 f == nil {
  32. fmt.Fprintf(os.Stderr, "cat: can't open %s: error %s\n", flag.Arg(i), err.Error())
  33. os.Exit(1)
  34. }
  35. cat(f)
  36. f.Close()
  37. }
  38. }

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

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

fmt.Fprintf() 函数的实际签名
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)

io.Writer 是接口类型

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

bufio.Writer 实现了 Write 方法:
func (b *Writer) Write(p []byte) (nn int, err error)

它还有一个工厂函数:传给它一个 io.Writer 类型的参数,它会返回一个缓冲的 bufio.Writer 类型的 io.Writer:
func NewWriter(wr io.Writer) (b *Writer)