Ввод и вывод в Go
package main
import "fmt"
func main() {
fmt.Println("Hello 世界!")
}Первый пример на этом сайте — вывод строки. В этом разделе рассмотрим, как выполнять ввод и вывод в 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
Поскольку стандартный вывод — это файл, можно напрямую записать строку в стандартный вывод:
package main
import "os"
func main() {
os.Stdout.WriteString("hello world!")
}print
В Go есть две встроенные функции: print и println. Они выводят параметры в стандартный вывод ошибок и предназначены только для отладки. Обычно их использование не рекомендуется.
package main
func main() {
print("hello world!\n")
println("hello world")
}fmt
Наиболее распространённый способ — использование пакета fmt. Он предоставляет функцию fmt.Println, которая по умолчанию выводит параметры в стандартный вывод.
package main
import "fmt"
func main() {
fmt.Println("hello world!")
}Параметры поддерживают любые типы. Если тип реализует интерфейс String, будет вызван метод String для получения строкового представления, поэтому вывод имеет высокую читаемость и подходит для большинства случаев. Однако из-за использования отражения внутри не рекомендуется массово использовать в сценариях, чувствительных к производительности.
bufio
bufio предоставляет буферизированный метод вывода. Данные сначала записываются в память, а после достижения определённого порога выводятся в указанный Writer. Размер буфера по умолчанию — 4KB. Рекомендуется использовать этот пакет для файлового IO и сетевого IO.
func main() {
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
writer.WriteString("hello world!")
}Можно также использовать его вместе с пакетом fmt:
func main() {
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
fmt.Fprintln(writer, "hello world!")
}Форматирование
Функции форматированного вывода в Go предоставляются функцией fmt.Printf. Если вы знакомы с языками семейства C, это покажется знакомым. Простой пример:
func main() {
fmt.Printf("hello world, %s!", "jack")
}Ниже приведены все форматирующие спецификаторы Go на данный момент.
| № | Формат | Описание | Тип получателя |
|---|---|---|---|
| 1 | %% | Вывод знака процента % | Любой |
| 2 | %s | Вывод значения string / []byte | string, []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 | Вывод символа, соответствующего коду Unicode | int32 |
| 23 | %U | Вывод кода Unicode символа | rune, byte |
| 24 | %p | Вывод адреса, на который указывает указатель | Указатель |
Используйте fmt.Sprintf или fmt.Printf для форматирования строк. Примеры:
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{})При использовании других систем счисления можно добавить пробел между % и спецификатором для получения разделителя. Например:
func main() {
str := "abcdefg"
fmt.Printf("%x\n", str)
fmt.Printf("% x\n", str)
}Результат:
61626364656667
61 62 63 64 65 66 67При работе с числами можно автоматически дополнять нулями. Например:
fmt.Printf("%09d", 1)
// 000000001То же для двоичной системы:
fmt.Printf("%09b", 1<<3)
// 000001000Ошибки
Количество спецификаторов меньше количества аргументов:
fmt.Printf("", "") // %!(EXTRA string=)Количество спецификаторов больше количества аргументов:
fmt.Printf("%s%s", "") // %!s(MISSING)Несоответствие типов:
fmt.Printf("%s", 1) // %!s(int=1)Отсутствует спецификатор:
fmt.Printf("%", 1) // %!(NOVERB)%!(EXTRA int=1)Ввод
Ниже приведены распространённые методы ввода.
read
Можно читать ввод так же, как читается файл:
func main() {
var buf [1024]byte
n, _ := os.Stdin.Read(buf[:])
os.Stdout.Write(buf[:n])
}Это слишком неудобно для использования, обычно не рекомендуется.
fmt
Можно использовать несколько функций из пакета fmt, похожих на C:
// Сканирует текст, прочитанный из 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)Чтение двух чисел:
func main() {
var a, b int
fmt.Scanln(&a, &b)
fmt.Printf("%d + %d = %d\n", a, b, a+b)
}Чтение массива фиксированной длины:
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:
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, но читает построчно:
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
exitTIP
Для практики ввода и вывода можно решить несколько простых алгоритмических задач в режиме ACM на Luogu.
