Отладочная печать
Самый старый способ сузить место дефекта — временно добавить в программу печать промежуточных значений. Он работает везде: на ноутбуке, на сервере, где отладчика нет, в программе, которая работает сутками. Правил два: печатать в поток ошибок и после работы убирать.
Блоки этой страницы с подписью «✎ Наберите в файл priemka/main.go» — это строки, которые вы дописываете в программу руками; куда именно, сказано в тексте перед блоком. Номера строк на страницах 3–4 считаются по оригиналу, поэтому перед ними программу вернём: cp ~/.course/orig/day09/priemka/main.go .
Печать в поток ошибок
Ситуация. В priemka нужно увидеть boxes и total после каждого прохода. Но обычный вывод программы — её результат. Его читает человек, его сохраняют в файл, его сверяет проверка курса. Если подмешать к нему отладочные строки, результат испортится.
В дне 07 у программы появился второй выход — поток ошибок, os.Stderr. На экране он виден так же, как обычный вывод, но командная строка умеет разделять их. Отладочную печать отправляют туда.
Добавьте в priemka/main.go одну строку — после total += qty, перед закрывающей скобкой цикла:
priemka/main.go fmt.Fprintln(os.Stderr, "отладка: строка", i, "qty", qty, "total", total)Пакет "os" в этой программе уже подключён. Слово отладка: в начале — метка: по ней отладочные строки легко найти и удалить.
Что увидите.
stagiaire@lab:~/gocourse/day09/priemka$ go run . < input.txt
отладка: строка 1 qty 12 total 12
отладка: строка 2 qty 8 total 20
отладка: строка 3 qty 20 total 40
отладка: строка 5 qty 15 total 55
отладка: строка 6 qty 9 total 64
отладка: строка 8 qty 11 total 75
отладка: строка 9 qty 30 total 105
отладка: строка 10 qty 6 total 111
Коробок: 8
Штук: 111Как это читать. Сравните с таблицей ожиданий со страницы 1. Строки 1–3 совпали. Строки 4 нет — так и задумано: пустая строка пропускается. После строки 6 total равен 64 — совпало. Следующая отладочная строка — сразу восьмая, и total в ней 75, а по таблице после восьмой должно быть 89: седьмая строка ввода прошла мимо total += qty. Первое расхождение — седьмая строка ввода.
Разделить потоки
Отладочные строки мешают читать результат. Командная строка может отправить поток ошибок отдельно: 2> — стрелка с номером потока. Номер 1 — обычный вывод, номер 2 — поток ошибок.
stagiaire@lab:~/gocourse/day09/priemka$ go run . < input.txt 2>/dev/null
Коробок: 8
Штук: 111
stagiaire@lab:~/gocourse/day09/priemka$ go run . < input.txt 2> debug.txt
Коробок: 8
Штук: 111
stagiaire@lab:~/gocourse/day09/priemka$ head -3 debug.txt
отладка: строка 1 qty 12 total 12
отладка: строка 2 qty 8 total 20
отладка: строка 3 qty 20 total 40/dev/null — особый файл: всё, что в него записано, пропадает. 2> debug.txt сохраняет отладку в файл — удобно, когда строк сотни: файл читают less или ищут в нём строку через grep.
Если напечатать отладку обычным fmt.Println("отладка: строка", i, "total", total), разделить уже не получится — 2>/dev/null её не уберёт:
stagiaire@lab:~/gocourse/day09/priemka$ go run . < input.txt 2>/dev/null | head -4
отладка: строка 1 total 12
отладка: строка 2 total 20
отладка: строка 3 total 40
отладка: строка 5 total 55| head -4 оставляет первые четыре строки вывода.
Попробуйте сейчас: отладка в поток ошибок.
Цель: увидеть, как
2>отделяет отладочную печать от результата, и предсказать вывод программы с отладкой.1. Добавьте отладочную строку в
priemkaи запустите тремя способами: без2>, с2>/dev/nullи с2> debug.txt. Посмотритеdebug.txt:▶ Выполнитеcd ~/gocourse/day09/priemka go run . < input.txt go run . < input.txt 2>/dev/null go run . < input.txt 2> debug.txt cat debug.txt2. Потом вставьте в
~/gocourse/day09/scratch/main.goпрограмму целиком и, не запуская, предскажите, что останется на экране приgo run . 2>/dev/null:✎ Наберите в файлscratch/main.gopackage main import ( "fmt" "os" ) func main() { total := 0 for i := 1; i <= 3; i++ { total += i * 10 fmt.Fprintln(os.Stderr, "отладка:", i, total) } fmt.Println("Итого", total) }3. Сдайте ответ:
▶ Выполните · выделенное замените своимcourse answer day09.q2 ОТВЕТВместо
ОТВЕТ— всё, что осталось на экране, одной строкой.4. Потом запустите и сверьте:
▶ Выполнитеcd ~/gocourse/day09/scratch go run . 2>/dev/nullГотово, когда: в
priemkaс2>/dev/nullна экране толькоКоробок:иШтук:, отладочные строки — вdebug.txt, а пунктыq2иt_stderrвcourse check 09зелёные.t_stderr— след команды: он смотрит, что вы запускали программу с2>, а не что в ней была отладочная печать.
Кавычки показывают пробелы: %q
Сужение показало место — седьмая строка ввода. Гипотеза со страницы 1: Atoi не понимает текст этой строки. Проверим печатью, что именно лежит в line.
У Printf есть глагол %q: он печатает строку в кавычках, и пробелы по краям и пустая строка становятся видны. Уберите прежнюю отладочную строку и добавьте новую сразу после line := scanner.Text():
priemka/main.go fmt.Fprintf(os.Stderr, "отладка: строка %d %q\n", i, line)fmt.Fprintf — тот же Printf, только первым аргументом указано, куда печатать. \n в конце — перевод строки: Printf сам его не ставит.
stagiaire@lab:~/gocourse/day09/priemka$ go run . < input.txt
отладка: строка 1 "12"
отладка: строка 2 "8"
отладка: строка 3 "20"
отладка: строка 4 ""
отладка: строка 5 "15"
отладка: строка 6 "9"
отладка: строка 7 "14 "
отладка: строка 8 "11"
отладка: строка 9 "30"
отладка: строка 10 "6"
Коробок: 8
Штук: 111"" — четвёртая строка пустая. "14 " — в седьмой после числа пробел. Можно напечатать и саму ошибку от Atoi. Уберите строку с %q и добавьте печать внутрь if err != nil, перед continue:
priemka/main.go fmt.Fprintln(os.Stderr, "отладка: строка", i, "ошибка:", err)stagiaire@lab:~/gocourse/day09/priemka$ go run . < input.txt
отладка: строка 4 ошибка: strconv.Atoi: parsing "": invalid syntax
отладка: строка 7 ошибка: strconv.Atoi: parsing "14 ": invalid syntax
Коробок: 8
Штук: 111Гипотеза подтверждена: Atoi получает "14 " и отвечает invalid syntax — «неверная запись числа».
Починка и уборка
Причина — пробел. Починка — строка 20: Atoi получает строку без пробелов по краям.
priemka/main.go qty, err := strconv.Atoi(strings.TrimSpace(line))TrimSpace — из пакета strings, а его в import этой программы нет. Блок import целиком становится таким:
priemka/main.goimport (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)Теперь уберите всю отладочную печать и прогоните исходный ввод:
stagiaire@lab:~/gocourse/day09/priemka$ go run . < input.txt
Коробок: 9
Штук: 125Затем — остальные вводы, на которых вы запускали программу раньше.
Почему отладочную печать убирают:
- поток ошибок нужен для сообщений пользователю вроде «Не число: …». Сотня отладочных строк их заглушит;
- проверка курса, как и многие инструменты, читает оба потока вместе. Забытая отладочная строка для неё — неверный вывод, и пункт задания станет красным;
- в работающем сервисе печать на каждом проходе цикла — это гигабайты журналов;
- забытая отладка — первое замечание на ревью кода.
Что изменилось по сравнению с оригиналом:
stagiaire@lab:~/gocourse/day09/priemka$ diff ~/.course/orig/day09/priemka/main.go main.go
9a10
> "strings"
20c21
< qty, err := strconv.Atoi(line)
---
> qty, err := strconv.Atoi(strings.TrimSpace(line))9a10 — после строки 9 оригинала добавлена строка, в новом файле она десятая. 20c21 — строка 20 изменилась и стала 21-й. Строки с < — как было, с > — как стало. Отладочных строк в diff нет — значит, уборка сделана.
Попробуйте сейчас: починить priemka, затем bug1.
Цель:
priemkaсчитает верно и без отладочной печати,bug1считает скидку верно.1. Почините
priemka, уберите отладку, прогоните, посмотритеdiff:▶ Выполнитеcd ~/gocourse/day09/priemka go run . < input.txt diff ~/.course/orig/day09/priemka/main.go main.go2. Затем задание
bug1:▶ Выполнитеcd ~/gocourse/day09/bug1 cat TASK.txt go run . < input.txt3. Пройдите шаги страницы 1. Составьте ожидание для каждой позиции на бумаге, напечатайте в поток ошибок промежуточные значения и найдите первую позицию, где они расходятся. Почините минимально, уберите отладку, прогоните снова и посмотрите правку:
▶ Выполнитеgo run . < input.txt diff ~/.course/orig/day09/bug1/main.go main.goГотово, когда:
priemkaпечатаетКоробок: 9иШтук: 125, а вdiffнет отладочных строк (пунктt_diff); выводbug1совпадает с «ожидалось» изTASK.txt, и вdiffс оригиналомbug1— одна-две изменённые строки: и то и другое смотрит пунктbug1. Формат сверитbugfmt— вместе сbug2иbug3.
Что может пойти не так
| Что видите | Что это значит | Что делать |
|---|---|---|
./main.go:7:15: undefined: os |
отладочная печать использует os, а в import его нет |
добавьте "os" в import |
./main.go:20:28: undefined: strings |
TrimSpace добавили, а пакет не подключили |
добавьте "strings" в import |
Отладочные строки не пропадают при 2>/dev/null |
печать через fmt.Println — в обычный вывод |
fmt.Fprintln(os.Stderr, …) |
course check пишет: строка 1, программа напечатала: «отладка: …» |
в задании осталась отладочная печать | удалите строки с меткой отладка: |
| Номера строк в сообщениях не совпадают со страницей | добавленные строки сдвинули всё, что ниже | для страниц 3–4 верните оригинал: cp ~/.course/orig/day09/priemka/main.go ~/gocourse/day09/priemka/ |
Дальше: отладчик dlv — course next