· 18 мин 👁 1.2k Начинающий

Язык Go — пакет fmt (часть 3)

Справочник по функциям пакета fmt — Append, Errorf, Fprint, Fprintf, Fprintln, Fscan, Fscanf, Fscanln, Print, Println, Printf, Sprint, Sprintf, Sprintln, Sscan, Sscanf, Sscanln и интерфейсы Formatter, GoStringer, Scanner, Stringer.

fmtфункцииerrorffprintfprintffscansprintsprintfstringerformatter
Содержание

Эта статья — справочник по функциям и интерфейсам пакета fmt. Для понимания глаголов форматирования, флагов и правил вывода см. часть 1, для сканирования — часть 2.

Функции

Append, Appendf, Appendln

func Append(b []byte, a ...any) []byte
func Appendf(b []byte, format string, a ...any) []byte
func Appendln(b []byte, a ...any) []byte

Добавляют форматированный результат к срезу байт и возвращают обновлённый срез. Добавлены в Go 1.19.

  • Append — формат по умолчанию; пробел между операндами добавляется только если ни один из них не является строкой
  • Appendf — форматирование по строке формата
  • Appendln — формат по умолчанию; пробелы добавляются всегда, в конце добавляется перенос строки

Errorf

func Errorf(format string, a ...any) (err error)

Форматирует строку по строке формата и возвращает её как значение типа error.

Если строка формата содержит глагол %w с операндом типа error, возвращаемая ошибка реализует метод Unwrap, возвращающий этот операнд. Если глаголов %w несколько — Unwrap вернёт []error со всеми такими операндами в порядке их появления в аргументах. Передавать в %w значение, не реализующее интерфейс error, недопустимо. Во всём остальном %w является синонимом %v.

package main

import (
	"fmt"
)

func main() {
	const name, id = "bueller", 17
	err := fmt.Errorf("user %q (id %d) not found", name, id)
	fmt.Println(err.Error())
	// Выведет: user "bueller" (id 17) not found
}

FormatString

func FormatString(state State, verb rune) string

Возвращает строку, представляющую полную директиву форматирования, захваченную State, с добавлением глагола. (Сам State глагол не содержит.) Результат начинается со знака %, за которым следуют флаги, ширина и точность — отсутствующие элементы опускаются. Функция позволяет типу, реализующему Formatter, восстановить исходную директиву, вызвавшую Format. Добавлена в Go 1.20.

Fprint

func Fprint(w io.Writer, a ...any) (n int, err error)

Форматирует аргументы в формате по умолчанию и пишет в w. Пробел между операндами добавляется только если ни один из них не является строкой. Возвращает количество записанных байт и ошибку записи, если она возникла.

package main

import (
	"fmt"
	"os"
)

func main() {
	const name, age = "Kim", 22
	n, err := fmt.Fprint(os.Stdout, name, " is ", age, " years old.\n")
	// Возвращаемые n и err — это значения, возвращённые
	// нижележащим io.Writer.
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fprint: %v\n", err)
	}
	fmt.Print(n, " bytes written.\n")
	// Выведет: Kim is 22 years old.
	// Выведет: 21 bytes written.
}

Fprintf

func Fprintf(w io.Writer, format string, a ...any) (n int, err error)

Форматирует аргументы по строке формата и пишет в w. Возвращает количество записанных байт и ошибку записи, если она возникла.

package main

import (
	"fmt"
	"os"
)

func main() {
	const name, age = "Kim", 22
	n, err := fmt.Fprintf(os.Stdout, "%s is %d years old.\n", name, age)
	// Возвращаемые n и err — это значения, возвращённые
	// нижележащим io.Writer.
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fprintf: %v\n", err)
	}
	fmt.Printf("%d bytes written.\n", n)
	// Выведет: Kim is 22 years old.
	// Выведет: 21 bytes written.
}

Fprintln

func Fprintln(w io.Writer, a ...any) (n int, err error)

Форматирует аргументы в формате по умолчанию и пишет в w. Пробелы добавляются всегда, в конце добавляется перенос строки. Возвращает количество записанных байт и ошибку записи, если она возникла.

package main

import (
	"fmt"
	"os"
)

func main() {
	const name, age = "Kim", 22
	n, err := fmt.Fprintln(os.Stdout, name, "is", age, "years old.")
	// Возвращаемые n и err — это значения, возвращённые
	// нижележащим io.Writer.
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fprintln: %v\n", err)
	}
	fmt.Println(n, "bytes written.")
	// Выведет: Kim is 22 years old.
	// Выведет: 21 bytes written.
}

Fscan

func Fscan(r io.Reader, a ...any) (n int, err error)

Читает текст из r, сохраняя разделённые пробелами значения в последовательные аргументы. Переносы строк считаются пробелами. Возвращает количество успешно просканированных элементов. Если оно меньше числа аргументов — err сообщит причину.

Fscanf

func Fscanf(r io.Reader, format string, a ...any) (n int, err error)

Читает текст из r, разбирая значения по строке формата. Возвращает количество успешно разобранных элементов. Переносы строк во вводе должны соответствовать переносам строк в формате.

package main

import (
	"fmt"
	"os"
	"strings"
)

func main() {
	var (
		i int
		b bool
		s string
	)
	r := strings.NewReader("5 true gophers")
	n, err := fmt.Fscanf(r, "%d %t %s", &i, &b, &s)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fscanf: %v\n", err)
	}
	fmt.Println(i, b, s) // Выведет: 5 true gophers
	fmt.Println(n)        // Выведет: 3
}

Fscanln

func Fscanln(r io.Reader, a ...any) (n int, err error)

Аналогична Fscan, но останавливается на переносе строки. После последнего элемента должен быть перенос строки или EOF.

package main

import (
	"fmt"
	"io"
	"strings"
)

func main() {
	s := `dmr 1771 1.61803398875
ken 271828 3.14159`
	r := strings.NewReader(s)
	var a string
	var b int
	var c float64
	for {
		n, err := fmt.Fscanln(r, &a, &b, &c)
		if err == io.EOF {
			break
		}
		if err != nil {
			panic(err)
		}
		fmt.Printf("%d: %s, %d, %f\n", n, a, b, c)
	}
	// Выведет: 3: dmr, 1771, 1.618034
	// Выведет: 3: ken, 271828, 3.141590
}

Print

func Print(a ...any) (n int, err error)

Форматирует аргументы в формате по умолчанию и пишет в стандартный вывод. Пробел между операндами добавляется только если ни один из них не является строкой. Возвращает количество записанных байт и ошибку записи, если она возникла.

package main

import (
	"fmt"
)

func main() {
	const name, age = "Kim", 22
	fmt.Print(name, " is ", age, " years old.\n")
	// По соглашению ошибку, возвращаемую Print, обычно игнорируют.
	// Выведет: Kim is 22 years old.
}

Printf

func Printf(format string, a ...any) (n int, err error)

Форматирует аргументы по строке формата и пишет в стандартный вывод. Возвращает количество записанных байт и ошибку записи, если она возникла.

package main

import (
	"fmt"
)

func main() {
	const name, age = "Kim", 22
	fmt.Printf("%s is %d years old.\n", name, age)
	// Выведет: Kim is 22 years old.
}

Println

func Println(a ...any) (n int, err error)

Форматирует аргументы в формате по умолчанию и пишет в стандартный вывод. Пробелы добавляются всегда, в конце добавляется перенос строки. Возвращает количество записанных байт и ошибку записи, если она возникла.

package main

import (
	"fmt"
)

func main() {
	const name, age = "Kim", 22
	fmt.Println(name, "is", age, "years old.")
	// По соглашению ошибку, возвращаемую Println, обычно игнорируют.
	// Выведет: Kim is 22 years old.
}

Scan

func Scan(a ...any) (n int, err error)

Читает текст из стандартного ввода, сохраняя разделённые пробелами значения в последовательные аргументы. Переносы строк считаются пробелами. Возвращает количество успешно просканированных элементов. Если оно меньше числа аргументов — err сообщит причину.

Scanf

func Scanf(format string, a ...any) (n int, err error)

Читает текст из стандартного ввода, разбирая значения по строке формата. Возвращает количество успешно просканированных элементов. Если оно меньше числа аргументов — err сообщит причину. Переносы строк во вводе должны соответствовать переносам строк в формате. Исключение: глагол %c всегда читает следующую руну из ввода, даже если это пробел, табуляция или перенос строки.

Scanln

func Scanln(a ...any) (n int, err error)

Аналогична Scan, но останавливается на переносе строки. После последнего элемента должен быть перенос строки или EOF.

Sprint

func Sprint(a ...any) string

Форматирует аргументы в формате по умолчанию и возвращает результат как строку. Пробел между операндами добавляется только если ни один из них не является строкой.

package main

import (
	"fmt"
)

func main() {
	const name, age = "Kim", 22
	s := fmt.Sprint(name, " is ", age, " years old.")
	fmt.Println(s)
	// Выведет: Kim is 22 years old.
}

Sprintf

func Sprintf(format string, a ...any) string

Форматирует аргументы по строке формата и возвращает результат как строку.

package main

import (
	"fmt"
)

func main() {
	const name, age = "Kim", 22
	s := fmt.Sprintf("%s is %d years old.", name, age)
	fmt.Println(s)
	// Выведет: Kim is 22 years old.
}

Sprintln

func Sprintln(a ...any) string

Форматирует аргументы в формате по умолчанию и возвращает результат как строку. Пробелы добавляются всегда, в конце добавляется перенос строки.

package main

import (
	"fmt"
)

func main() {
	const name, age = "Kim", 22
	s := fmt.Sprintln(name, "is", age, "years old.")
	fmt.Print(s)
	// Выведет: Kim is 22 years old.
}

Sscan

func Sscan(str string, a ...any) (n int, err error)

Читает значения из строки str, сохраняя разделённые пробелами элементы в последовательные аргументы. Переносы строк считаются пробелами. Возвращает количество успешно просканированных элементов. Если оно меньше числа аргументов — err сообщит причину.

Sscanf

func Sscanf(str string, format string, a ...any) (n int, err error)

Читает значения из строки str, разбирая их по строке формата. Возвращает количество успешно разобранных элементов. Переносы строк во вводе должны соответствовать переносам строк в формате.

package main

import (
	"fmt"
)

func main() {
	var name string
	var age int
	n, err := fmt.Sscanf("Kim is 22 years old.", "%s is %d", &name, &age)
	if err != nil {
		panic(err)
	}
	fmt.Println(n)        // Выведет: 2
	fmt.Println(name, age) // Выведет: Kim, 22
}

Sscanln

func Sscanln(str string, a ...any) (n int, err error)

Аналогична Sscan, но останавливается на переносе строки. После последнего элемента должен быть перенос строки или EOF.

Интерфейсы

Formatter

type Formatter interface {
    Format(f State, verb rune)
}

Реализуется любым типом, имеющим метод Format. Реализация сама управляет интерпретацией State и глагола, и может вызывать Sprint, Fprint(f) и т.д. для формирования вывода.

GoStringer

type GoStringer interface {
    GoString() string
}

Реализуется любым типом, имеющим метод GoString, определяющий Go-синтаксис для данного значения. Метод вызывается при форматировании значения глаголом %#v.

package main

import "fmt"

type Address struct {
	City    string
	State   string
	Country string
}

type Person struct {
	Name string
	Age  uint
	Addr *Address
}

// GoString реализует интерфейс fmt.GoStringer
// и возвращает представление в синтаксисе Go.
func (p Person) GoString() string {
	if p.Addr != nil {
		return fmt.Sprintf("Person{Name: %q, Age: %d, Addr: &Address{City: %q, State: %q, Country: %q}}", p.Name, int(p.Age), p.Addr.City, p.Addr.State, p.Addr.Country)
	}
	return fmt.Sprintf("Person{Name: %q, Age: %d}", p.Name, int(p.Age))
}

func main() {
	p1 := Person{
		Name: "Warren",
		Age:  31,
		Addr: &Address{
			City:    "Denver",
			State:   "CO",
			Country: "U.S.A.",
		},
	}
	p2 := Person{
		Name: "Theia",
		Age:  4,
	}
	fmt.Printf("%#v\n", p1)
	// Выведет: Person{Name: "Warren", Age: 31, Addr: &Address{City: "Denver", State: "CO", Country: "U.S.A."}}
	fmt.Printf("%#v\n", p2)
	// Выведет: Person{Name: "Theia", Age: 4}
}

ScanState

type ScanState interface {
    // ReadRune читает следующую руну (кодовую точку Unicode) из ввода.
    // При вызове во время Scanln, Fscanln или Sscanln возвращает EOF
    // после первого '\n' или при выходе за пределы заданной ширины.
    ReadRune() (r rune, size int, err error)
    // UnreadRune заставляет следующий вызов ReadRune вернуть ту же руну.
    UnreadRune() error
    // SkipSpace пропускает пробельные символы во вводе. Переносы строк
    // обрабатываются в зависимости от выполняемой операции — подробнее
    // в документации пакета.
    SkipSpace()
    // Token пропускает пробелы (если skipSpace равен true), затем возвращает
    // последовательность рун c, удовлетворяющих f(c). Если f равен nil,
    // используется !unicode.IsSpace(c) — токен содержит непробельные символы.
    // Переносы строк обрабатываются в зависимости от операции.
    // Возвращаемый срез указывает на общие данные, которые могут быть
    // перезаписаны следующим вызовом Token, функции сканирования с тем же
    // ScanState или при возврате из вызывающего метода Scan.
    Token(skipSpace bool, f func(rune) bool) (token []byte, err error)
    // Width возвращает значение ширины и флаг её наличия.
    // Единица измерения — кодовые точки Unicode.
    Width() (wid int, ok bool)
    // Поскольку ReadRune реализован через интерфейс, Read не должен
    // вызываться функциями сканирования; корректная реализация ScanState
    // вправе всегда возвращать ошибку из Read.
    Read(buf []byte) (n int, err error)
}

ScanState представляет состояние сканера, передаваемое пользовательским сканерам. Сканер может читать ввод руна за руной или запрашивать у ScanState следующий токен, ограниченный пробелами.

Scanner

type Scanner interface {
    Scan(state ScanState, verb rune) error
}

Реализуется любым типом, имеющим метод Scan, который читает из ввода представление значения и сохраняет результат в получателе — получатель должен быть указателем. Метод вызывается для любого аргумента функций Scan, Scanf или Scanln, реализующего данный интерфейс.

State

type State interface {
    // Write — функция для записи форматированного вывода.
    Write(b []byte) (n int, err error)
    // Width возвращает значение ширины и флаг её наличия.
    Width() (wid int, ok bool)
    // Precision возвращает значение точности и флаг её наличия.
    Precision() (prec int, ok bool)
    // Flag сообщает, установлен ли флаг c (символ).
    Flag(c int) bool
}

State представляет состояние принтера, передаваемое пользовательским форматтерам. Предоставляет доступ к интерфейсу io.Writer, а также к флагам и параметрам спецификатора формата операнда.

Stringer

type Stringer interface {
    String() string
}

Реализуется любым типом, имеющим метод String, определяющий «родной» формат значения. Метод вызывается при форматировании значения любым глаголом, принимающим строку, или при неформатированном выводе, например через Print.

package main

import "fmt"

// Animal содержит название животного и количество ног.
type Animal struct {
	Name string
	Legs int
}

// String реализует интерфейс fmt.Stringer.
func (a Animal) String() string {
	return fmt.Sprintf("%v (%d)", a.Name, a.Legs)
}

func main() {
	zebra := Animal{"Gopher", 2}
	fmt.Println(zebra)
	// Выведет: Gopher (2)
}

Итоги

  • Append* — добавляют результат к срезу байт (с Go 1.19)
  • Errorf поддерживает %w для оборачивания ошибок; несколько %w вернут []error через Unwrap
  • Fprint* / Fprintf* / Fprintln* пишут в io.Writer и возвращают (n int, err error)
  • Print* / Printf* / Println* пишут в os.Stdout
  • Sprint* / Sprintf* / Sprintln* возвращают строку
  • Fscan* / Scan* / Sscan* — сканирование из io.Reader, stdin и строки соответственно
  • Stringer (String() string) — автоматически вызывается при выводе; GoStringer (GoString() string) — при %#v
  • Formatter позволяет полностью управлять форматированием через метод Format(State, rune)
  • Scanner позволяет типу самому управлять разбором ввода через метод Scan(ScanState, rune)

Предыдущий шаг: Пакет fmt (часть 2)