День 19 · Строки, руны и файлы · страница 6 из 6

Отчёт по выгрузке и битые строки

Строка режется на поля, название доводится до ширины, файл читается по имени. Осталось собрать отчёт и решить, что делать с битой строкой.


Битая строка — не повод останавливаться

Ситуация. В выгрузке десять тысяч строк, в двух оператор ошибся. Остановится программа на первой — отчёта нет, о второй ошибке никто не узнает. Молча пропустит — итог соврёт, и никто не узнает почему.

Поэтому правило такое:

  1. битая строка — сообщение в поток ошибок с номером строки, чтобы её можно было найти в файле;
  2. программа идёт дальше — к следующей строке;
  3. после всех строк — итог и код возврата 1, если битые строки были: скрипт, запустивший отчёт, узнает, что выгрузка грязная.

Цикл отчёта:

Код для чтения · разбираем, набирать не нужно
	for i, line := range lines {
		name, qty, price, err := parseLine(line)
		if err != nil {
			fmt.Fprintf(os.Stderr, "строка %d: %v\n", i+1, err)
			bad++
			continue
		}
		fmt.Printf("%s %d шт. %d коп.\n", fitName(name, width), qty, qty*price)
		count++
		total += qty * price
	}
	fmt.Printf("Итого: %d поз., %d коп.\n", count, total)
	if bad > 0 {
		os.Exit(1)
	}

Как это читать.

Строка Зачем
i+1 range считает с нуля, а строки файла — с единицы: человек откроет файл на строке 5, а не 4
bad++ и continue запомнили, что была ошибка, и перешли к следующей строке — continue из дня 08
Fprintf(os.Stderr, …) сообщение не попадёт в файл отчёта при > report.txt — день 07
os.Exit(1) после Итого os.Exit завершает программу сразу: раньше печати итога — итога не будет

Отчёт целиком

Программа целиком — задание страницы. Её запуск с шириной 14 на таком orders.csv:

Образец · набирать не нужно
TK-101;Болт М8;12;1500
TK-102;Анкер клиновой;4;2750
TK-103;Кронштейн усиленный;200;45
TK-104;Bolt-M8;10;1400
TK-105;Шайба кровельная;100
TK-106;Хомут червячный;сто;90
TK-107;Шайба;40;350

Что увидите.

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day19/report$ go run .
Болт М8....... 12 шт. 18000 коп.
Анкер клиновой 4 шт. 11000 коп.
Кронштейн уси~ 200 шт. 9000 коп.
Bolt-M8....... 10 шт. 14000 коп.
строка 5: нужно 4 поля, а их 3
строка 6: количество: strconv.Atoi: parsing "сто": invalid syntax
Шайба......... 40 шт. 14000 коп.
Итого: 5 поз., 66000 коп.
exit status 1

На экране оба потока вперемешку, в порядке печати. Разделить их — как в дне 07:

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day19/report$ go build -o prog . && ./prog > report.txt; echo $?
строка 5: нужно 4 поля, а их 3
строка 6: количество: strconv.Atoi: parsing "сто": invalid syntax
1
stagiaire@lab:~/gocourse/day19/report$ cat report.txt
Болт М8....... 12 шт. 18000 коп.
Анкер клиновой 4 шт. 11000 коп.
Кронштейн уси~ 200 шт. 9000 коп.
Bolt-M8....... 10 шт. 14000 коп.
Шайба......... 40 шт. 14000 коп.
Итого: 5 поз., 66000 коп.

Как это читать. В report.txt — чистый отчёт, сообщения с номерами строк — на экране. Код 1: отчёт есть, но в выгрузке ошибки.

Для сравнения — та же программа, где вместо bad++ и continue стоит os.Exit(1):

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day19/report$ go run .
Болт М8....... 12 шт. 18000 коп.
Анкер клиновой 4 шт. 11000 коп.
Кронштейн уси~ 200 шт. 9000 коп.
Bolt-M8....... 10 шт. 14000 коп.
строка 5: нужно 4 поля, а их 3
exit status 1

Ни шестой строки с ошибкой, ни седьмой верной, ни итога.


Попробуйте сейчас: задание report.

Цель: main.go в ~/gocourse/day19/report печатает отчёт по вашему orders.csv ровно как в TASK.txt: колонка по буквам, битые строки в поток ошибок, код возврата.

1. Перечитайте условие, особенно пример — ширина колонки у вас своя:

▶ Выполните
cd ~/gocourse/day19/report
cat TASK.txt
cat orders.csv

2. Напишите main.go: чтение orders.csv — любым способом страницы 5, цикл — из раздела выше: parseLine на каждую строку, верную — через fitName, битую — в поток ошибок, в конце итог и код.

3. Проверьте на примере из условия и на файле без битых строк:

▶ Выполните
go run .
go build -o prog .
./prog > out.txt; echo $?
cat out.txt

4. Проверьте ошибку открытия: переименуйте файл, запустите и верните имя:

▶ Выполните
mv orders.csv orders.bak
go run .
mv orders.bak orders.csv

5. Добавьте в orders.csv свою битую строку — с пятью полями или ценой 9,90: номер в сообщении обязан совпасть с номером строки в файле. Потом уберите её.

Готово, когда: вывод, поток ошибок и код совпали с примером в TASK.txt; без файла — ошибка: open orders.csv: no such file or directory и код 1 (пункты report и broken).


Попробуйте сейчас: ритуал и коммит.

Цель: весь код дня отформатирован, go vet молчит, тесты зелёные, работа записана.

▶ Выполните
cd ~/gocourse/day19
gofmt -l .
go vet ./...
go test ./...
git add .
git commit -m "report: отчёт по выгрузке"
git log --oneline

Готово, когда: gofmt -l . и go vet ./... ничего не печатают, go test ./... — ok у day19/report, в git log --oneline не меньше четырёх коммитов (пункты code_fmt и git).


Что может пойти не так

Что видите Что это значит Что делать
нет строки Итого os.Exit(1) стоит внутри цикла или раньше печати итога bad++ и continue в цикле, os.Exit(1) — последней строкой main
номера строк в сообщениях на один меньше, чем в файле печатается i, а range считает с нуля i+1
сообщения о битых строках оказались в out.txt ошибка напечатана через fmt.Printf fmt.Fprintf(os.Stderr, …)
echo $? печатает 0, хотя битые строки были в конце нет os.Exit(1) if bad > 0 { os.Exit(1) } после итога
в колонке � название обрезано по байтам обрезать через []rune — fitName со страницы 3
на вашем файле колонка ровная, а проверка показывает кривую на другом число точек вписано под свои названия или считается через len всё через fitName(название, ширина)
проверка: лишняя битая строка с номером за концом файла Split после последнего перевода строки TrimSuffix, как в fix2
declared and not used: bad счётчик ошибок заведён, но не проверяется в конце if bad > 0 { os.Exit(1) }

Словарик ошибок

Допишите в ~/errors.md сообщения дня, которые встретили сами, — вместе с прошлыми днями не меньше тридцати восьми строк (пункт errors). Строка — «текст ошибки — что значило», через тире с пробелами:

Образец · набирать не нужно
panic: strings: negative Repeat count — ширину считал через len, у русского названия байтов больше ширины
open orders.csv: no such file or directory — программа запущена не из того каталога, где лежит файл
invalid operation: r + "!" (mismatched types rune and untyped string) — руна не строка, нужен string(r)

Материал дня закончен. Задания — course lab 19, дополнительное чтение — course extra 19