Skip to content

Go輸入輸出

go
package main

import "fmt"

func main() {
   fmt.Println("Hello 世界!")
}

本站的第一個入門的案例就是輸出一個字符串,這一節就來講一下在 Go 中如何進行輸入輸出。

文件描述符

go
var (
   Stdin  = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
   Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
   Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)

os包下有三個外暴露的文件描述符,其類型都是*os.File,分別是:

  • os.Stdin - 標准輸入
  • os.Stdout - 標准輸出
  • os.Stderr - 標准錯誤

Go 中的輸入輸出都離不開它們。

輸出

在 Go 中輸出有很多中方法,下面幾個比較常見的

stdout

因為標准輸出本身就是一個文件,所以你可以直接將字符串寫入到標准輸出中

go
package main

import "os"

func main() {
  os.Stdout.WriteString("hello world!")
}

print

Go 有兩個內置的函數printprintln,他們會將參數輸出到標准錯誤中,僅做調試用,一般不推薦使用。

go
package main

func main() {
  print("hello world!\n")
  println("hello world")
}

fmt

最常見的用法是使用fmt包,它提供了fmt.Println函數,該函數默認會將參數輸出到標准輸出中。

go
package main

import "fmt"

func main() {
  fmt.Println("hello world!")
}

它的參數支持任意類型,如果類型實現了String接口也會調用String方法來獲取其字符串表現形式,所以它輸出的內容可讀性比較高,適用於大部分情況,不過由於內部用到了反射,在性能敏感的場景不建議大量使用。

bufio

bufio提供了可緩沖的輸出方法,它會先將數據寫入到內存中,積累到了一定閾值再輸出到指定的Writer中,默認緩沖區大小是4KB。在文件 IO,網絡 IO 的時候建議使用這個包。

go
func main() {
  writer := bufio.NewWriter(os.Stdout)
  defer writer.Flush()
  writer.WriteString("hello world!")
}

你也可以把它和fmt包結合起來用

go
func main() {
  writer := bufio.NewWriter(os.Stdout)
  defer writer.Flush()
  fmt.Fprintln(writer, "hello world!")
}

格式化

Go 中的格式化輸出功能基本上由fmt.Printf函數提供,如果你學過 C 系語言,一定會覺得很熟悉,下面是一個簡單的例子。

go
func main() {
  fmt.Printf("hello world, %s!", "jack")
}

下面是 Go 目前所有的格式化動詞。

0格式化描述接收類型
1%%輸出百分號%任意
2%s輸出string/[] bytestring,[] byte
3%q格式化字符串,輸出的字符串兩端有雙引號""string,[] byte
4%d輸出十進制整型值整型
5%f輸出浮點數浮點
6%e輸出科學計數法形式 ,也可以用於復數浮點
7%E%e相同浮點
8%g根據實際情況判斷輸出%f或者%e,會去掉多余的 0浮點
9%b輸出整型的二進制表現形式數字
10%#b輸出二進制完整的表現形式數字
11%o輸出整型的八進制表示整型
12%#o輸出整型的完整八進制表示整型
13%x輸出整型的小寫十六進制表示數字
14%#x輸出整型的完整小寫十六進制表示數字
15%X輸出整型的大寫十六進制表示數字
16%#X輸出整型的完整大寫十六進制表示數字
17%v輸出值原本的形式,多用於數據結構的輸出任意
18%+v輸出結構體時將加上字段名任意
19%#v輸出完整 Go 語法格式的值任意
20%t輸出布爾值布爾
21%T輸出值對應的 Go 語言類型值任意
22%c輸出 Unicode 碼對應的字符int32
23%U輸出字符對應的 Unicode 碼rune,byte
24%p輸出指針所指向的地址指針

使用fmt.Sprintf或者fmt.Printf來格式化字符串或者輸出格式化字符串,看幾個例子

go
fmt.Printf("%%%s\n", "hello world")

fmt.Printf("%s\n", "hello world")
fmt.Printf("%q\n", "hello world")
fmt.Printf("%d\n", 2<<7-1)

fmt.Printf("%f\n", 1e2)
fmt.Printf("%e\n", 1e2)
fmt.Printf("%E\n", 1e2)
fmt.Printf("%g\n", 1e2)

fmt.Printf("%b\n", 2<<7-1)
fmt.Printf("%#b\n", 2<<7-1)
fmt.Printf("%o\n", 2<<7-1)
fmt.Printf("%#o\n", 2<<7-1)
fmt.Printf("%x\n", 2<<7-1)
fmt.Printf("%#x\n", 2<<7-1)
fmt.Printf("%X\n", 2<<7-1)
fmt.Printf("%#X\n", 2<<7-1)

type person struct {
    name    string
    age     int
    address string
}
fmt.Printf("%v\n", person{"lihua", 22, "beijing"})
fmt.Printf("%+v\n", person{"lihua", 22, "beijing"})
fmt.Printf("%#v\n", person{"lihua", 22, "beijing"})
fmt.Printf("%t\n", true)
fmt.Printf("%T\n", person{})
fmt.Printf("%c%c\n", 20050, 20051)
fmt.Printf("%U\n", '')
fmt.Printf("%p\n", &person{})

使用其它進制時,在%與格式化動詞之間加上一個空格便可以達到分隔符的效果,例如

go
func main() {
  str := "abcdefg"
  fmt.Printf("%x\n", str)
  fmt.Printf("% x\n", str)
}

該例輸出的結果為

61626364656667
61 62 63 64 65 66 67

在使用數字時,還可以自動補零。比如

go
fmt.Printf("%09d", 1)
// 000000001

二進制同理

go
fmt.Printf("%09b", 1<<3)
// 000001000

錯誤情況

格式化字符數量 < 參數列表數量

go
fmt.Printf("", "") //%!(EXTRA string=)

格式化字符數量 > 參數列表數量

go
fmt.Printf("%s%s", "") //%!s(MISSING)

類型不匹配

go
fmt.Printf("%s", 1) //%!s(int=1)

缺少格式化動詞

go
fmt.Printf("%", 1) // %!(NOVERB)%!(EXTRA int=1)

輸入

下面介紹常見的輸入方法

read

你可以像直接讀文件一樣,讀取輸入內容,如下

go
func main() {
  var buf [1024]byte
  n, _ := os.Stdin.Read(buf[:])
  os.Stdout.Write(buf[:n])
}

這樣用起來太麻煩了,一般不推薦使用。

fmt

我們可以使用fmt包提供的幾個函數,用起來跟 C 差不多。

go
// 掃描從os.Stdin讀入的文本,根據空格分隔,換行也被當作空格
func Scan(a ...any) (n int, err error)

// 與Scan類似,但是遇到換行停止掃描
func Scanln(a ...any) (n int, err error)

// 根據格式化的字符串掃描
func Scanf(format string, a ...any) (n int, err error)

讀取兩個數字

go
func main() {
  var a, b int
  fmt.Scanln(&a, &b)
  fmt.Printf("%d + %d = %d\n", a, b, a+b)
}

讀取固定長度的數組

go
func main() {
  n := 10
  s := make([]int, n)
  for i := range n {
    fmt.Scan(&s[i])
  }
  fmt.Println(s)
}
1 2 3 4 5 6 7 8 9 10
[1 2 3 4 5 6 7 8 9 10]

bufio

在有大量輸入需要讀取的時候,就建議使用bufio.Reader來進行內容讀取

go
func main() {
    reader := bufio.NewReader(os.Stdin)
    var a, b int
    fmt.Fscanln(reader, &a, &b)
    fmt.Printf("%d + %d = %d\n", a, b, a+b)
}

scanner

bufio.Scannerbufio.Reader類似,不過它是按行讀取的。

go
func main() {
  scanner := bufio.NewScanner(os.Stdin)
  for scanner.Scan() {
    line := scanner.Text()
    if line == "exit" {
      break
    }
    fmt.Println("scan", line)
  }
}

結果如下

first line
scan first line
second line
scan second line
third line
scan third line
exit

TIP

在輸入輸出這方面,想要練手的話,去洛谷做幾道簡單的 ACM 模式算法題就能上手熟悉了。

Golang學習網由www.golangdev.cn整理維護