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 есть две встроенные функции: print и println. Они выводят параметры в стандартный вывод ошибок и предназначены только для отладки. Обычно их использование не рекомендуется.

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 на данный момент.

ФорматОписаниеТип получателя
1%%Вывод знака процента %Любой
2%sВывод значения string / []bytestring, []byte
3%qФорматированная строка в двойных кавычках ""string, []byte
4%dВывод десятичного целого числаЦелочисленный
5%fВывод числа с плавающей точкойС плавающей точкой
6%eВывод в экспоненциальной форме, также для комплексных чиселС плавающей точкой
7%EАналогично %eС плавающей точкой
8%gАвтоматический выбор %f или %e, удаляет лишние нулиС плавающей точкой
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Вывод символа, соответствующего коду Unicodeint32
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.Scanner похож на bufio.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 на Luogu.

Golang by www.golangdev.cn edit