Строки и fmt.Printf
Чек, накладная, сообщение об остатке — текст с числами внутри. Println для этого неудобен: ставит пробелы, где не просят, и не умеет «05 коп.». Сегодня — fmt.Printf, печать по шаблону, и go vet, который находит ошибки шаблонов до запуска.
Склейка строк
Оператор + у строк не складывает, а склеивает.
package main
import "fmt"
func main() {
name := "Север"
title := "Склад " + name
fmt.Println(title)
fmt.Println("12"+"5", 12+5)
}stagiaire@lab:~/gocourse/day03/scratch$ go run .
Склад Север
125 17Как это читать. Пробел между «Склад» и «Север» — внутри первой строки: + своих пробелов не добавляет. "12"+"5" — склейка строк, 125; 12+5 — сумма чисел, 17. Оператор один, действие разное — решает тип. А строку с числом сложить нельзя:
n := 5
fmt.Println("Итого: " + n)stagiaire@lab:~/gocourse/day03/scratch$ go run .
# day03/scratch
./main.go:7:14: invalid operation: "Итого: " + n (mismatched types untyped string and int)С двумя переменными сообщение то же, только без untyped. Выхода два: печатать через запятую, как в день 01, или через Printf.
fmt.Printf: шаблон и значения
Ситуация. Нужна строка Склад Север: 12 шт. — двоеточие вплотную к имени, а Println поставит перед ним пробел. Printf печатает шаблон с местами для значений:
name := "Север"
qty := 12
weight := 2.5
fmt.Printf("Склад %s: %d шт.\n", name, qty)
fmt.Printf("Вес: %.2f кг\n", weight)
fmt.Printf("%v | %v | %v | %v\n", name, qty, weight, true)
fmt.Printf("%T %T %T %T\n", name, qty, weight, true)Склад Север: 12 шт.
Вес: 2.50 кг
Север | 12 | 2.5 | true
string int float64 boolКак это читать. Первый аргумент — шаблон, а каждое % с буквой — глагол: место для следующего значения из списка, по порядку.
| Глагол | Что печатает | Пример |
|---|---|---|
%d |
целое число (decimal) | 12 |
%s |
строку | Север |
%.2f |
дробное, ровно два знака после точки, с округлением | 2.50 |
%02d |
целое, не меньше двух цифр, недостающие — нули | 05 |
%v |
любое значение, как его напечатал бы Println |
2.5, true |
%T |
тип значения | float64 |
%% |
сам знак % |
% |
Вот и способ узнать тип, обещанный на первой странице: %T.
Printf ничего не добавляет сам — ни пробелов, ни перевода строки. Поэтому шаблон почти всегда кончается на \n: без него к выводу приклеится приглашение терминала:
stagiaire@lab:~/gocourse/day03/scratch$ go run .
Итого: 5stagiaire@lab:~/gocourse/day03/scratch$Копейки печатает %02d: ширина 2 — «не меньше двух символов», 0 перед ней — «дополнить нулями». Пятёрка станет 05, 45 останется 45, а %d напечатает 5. Округление у %.2f настоящее: 18.3366 даёт 18.34, 7.0 — 7.00.
Попробуйте сейчас: печать по шаблону.
Цель: вывод
Printfсовпал с показанным выше, и видно, что делают\nи%02d.
- Вставьте в тело
mainстроки из примера соСклад Севери три строки про копейки:✎ Наберите в файлscratch/main.goname := "Север" qty := 12 weight := 2.5 fmt.Printf("Склад %s: %d шт.\n", name, qty) fmt.Printf("Вес: %.2f кг\n", weight) fmt.Printf("%v | %v | %v | %v\n", name, qty, weight, true) fmt.Printf("%T %T %T %T\n", name, qty, weight, true) fmt.Printf("%d руб. %02d коп.\n", 123, 5) fmt.Printf("%d руб. %d коп.\n", 123, 5) fmt.Printf("Скидка: %d%%\n", 15)
- Запустите и сверьте первые четыре строки с выводом выше, последние три — между собой:
▶ Выполнитеcd ~/gocourse/day03/scratch go run .Вывод · это печатает программа, набирать не нужно123 руб. 05 коп. 123 руб. 5 коп. Скидка: 15%
- Уберите
\nв конце первого шаблона и запустите ещё раз: где теперь начинается строкаВес?Готово, когда: вывод совпал с показанным и видно, где начинается строка
Веспосле второго запуска.
Ошибка в шаблоне: компилятор молчит
Шаблон — обычная строка, и компилятор в неё не заглядывает: программа с неправильным шаблоном собирается, запускается и печатает на месте ошибки странную вставку. В строке, которая печатается раз в месяц, такую ошибку никто не заметит, пока она не попадёт в чек покупателю.
Для этого есть go vet: он читает код, не запуская его, и ищет подозрительные места, которые компилятор пропускает; шаблоны Printf — главное из них.
avg := 3.5
sum := 10
fmt.Printf("Средняя: %d\n", avg)
fmt.Printf("Сумма: %.2f\n", sum)stagiaire@lab:~/gocourse/day03/scratch$ go run .
Средняя: %!d(float64=3.5)
Сумма: %!f(int=10)
stagiaire@lab:~/gocourse/day03/scratch$ go vet .
main.go:8:30: fmt.Printf format %d has arg avg of wrong type float64
main.go:9:26: fmt.Printf format %.2f has arg sum of wrong type intКак это читать. Вставка в выводе: %! — «ошибка», дальше глагол, в скобках — что пришло вместо нужного. Сообщение vet — в знакомом формате: файл, строка, колонка, текст, только без строки # day03/scratch и без ./. Дословно: «формат %d получил аргумент avg неверного типа float64». Когда всё в порядке, go vet . молчит.
Ещё три находки vet — глагол без значения (%!d(MISSING)), лишнее значение (%!(EXTRA int=7)) и глагол в Println вместо Printf — с текстами сообщений разобраны в course extra 03.
Привычка на весь курс: перед сдачей — go vet ..
Что может пойти не так
| Что видите | Что это значит | Что делать |
|---|---|---|
invalid operation: "Итого: " + n (mismatched types untyped string and int) |
склеиваете строку с числом | Println("Итого:", n) или Printf("Итого: %d\n", n) |
| приглашение приклеилось к выводу | в шаблоне нет \n |
добавить \n |
%!d(MISSING), %!d(string=…), %!(EXTRA …) |
шаблон и значения не сошлись | go vet . покажет строку и причину |
в выводе 75%!, на следующей строке (MISSING) |
одиночный % перед \n принят за начало глагола |
знак процента — %% |
5 коп. вместо 05 коп. |
%d не дополняет нулями |
%02d |
Попробуйте сейчас: go vet.
Цель:
go vet .нашёл ошибку в шаблоне, а после правки промолчал.
- В
scratch/main.goнапишите:✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { total := 5 fmt.Println("Итого: %d", total) }
- Запустите программу и
go vet:▶ Выполнитеcd ~/gocourse/day03/scratch go run . go vet .
go run .напечатает строку с%dвнутри,go vet .назовёт причину.
- Замените
PrintlnнаPrintf, допишите\nв шаблон и запуститеgo vet .снова — он должен промолчать:▶ Выполнитеgo vet .
- Сообщение
vet— в словарик, через тире.Готово, когда: второй
go vet .ничего не напечатал; запуск засчитан в пункте проверкиt_vet, в~/errors.mdновая строка с тире.
Попробуйте сейчас: задание
avg.Цель:
avg/main.goпечатает сумму и среднюю цену поTASK.txt, аgo vet .молчит.
- Прочитайте условие — средняя цена трёх позиций:
▶ Выполнитеcd ~/gocourse/day03/avg cat TASK.txt
- Напишите программу в
avg/main.go. Цены и сумма в копейках — целые. Средняя цена — величина не для учёта, а для показа: её считают вfloat64до деления, и%.2fокругляет её до копейки. Печать —Printfс%dи%.2f.- Перед сдачей:
▶ Выполнитеgo run . go vet .Готово, когда: вывод совпал с тем, что требует
TASK.txt, аgo vet .ничего не напечатал — пункты проверкиavgиvet.
Дальше: как считать деньги — course next