Строки ввода до конца
В день 07 программа читала столько строк, сколько раз написано scanner.Scan(). В накладной их сколько угодно — значит, Scan встанет в заголовок цикла.
for scanner.Scan()
Ситуация. Обработать каждую строку ввода, сколько бы их ни было.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
fmt.Println("прочитал:", scanner.Text())
}
fmt.Println("ввод кончился")
}Что увидите.
stagiaire@lab:~/gocourse/day08/scratch$ printf '12\n7\n3\n' | go run .
прочитал: 12
прочитал: 7
прочитал: 3
ввод кончилсяКак это читать. В день 07 вы вызывали scanner.Scan() отдельной строкой и не смотрели, что он возвращает. А возвращает он bool: true — строка прочитана, false — читать нечего. Поэтому вызов и годится в условие, как stock > 0 на странице 3: пока Scan() отдаёт true, выполняется тело, и scanner.Text() в нём даёт прочитанную строку без перевода строки.
Где кончается ввод, зависит от того, откуда он пришёл.
| Откуда ввод | Как запускали | Когда Scan() вернёт false |
|---|---|---|
| файл | go run . < lines.txt |
после последней строки файла |
| конвейер | printf … | go run . |
когда printf закончил печатать |
| клавиатура | go run . |
когда нажали Ctrl+D в начале строки |
Попробуйте сейчас: ввод из конвейера и из файла.
Цель: программа читает строки и из
printf, и из файла.1. Наберите её в
scratch/main.go— файлы для опытов там уже лежат:✎ Наберите в файлscratch/main.gopackage main import ( "bufio" "fmt" "os" ) func main() { scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { fmt.Println("прочитал:", scanner.Text()) } fmt.Println("ввод кончился") }2. Запустите:
▶ Выполнитеcd ~/gocourse/day08/scratch printf '12\n7\n' | go run . go run . < lines.txtВ
lines.txtвторая строка пустая:Text()вернул пустую строку, и цикл её не пропустил.Готово, когда: оба запуска кончаются строкой
ввод кончился, а во втором вторая строка —прочитал:без числа; пунктыt_fileиt_pipe.
С клавиатуры: Ctrl+D
Без < и | строки читаются с клавиатуры: каждая уходит по Enter, конец ввода — Ctrl+D в начале новой строки. Запустили и ничего не набрали — программа молчит: это не зависание, она ждёт строку. Ctrl+D значит «ввод кончился», цикл заканчивается и итог печатается; Ctrl+C — «прекратить», строка после цикла не выполнится. Оба сеанса — в course extra 08.
Пустые строки и ошибка в строке
Ситуация. В выгрузке бывают пустые строки, пробелы по краям и опечатки вроде 3шт: пустые пропустить, на опечатке остановиться и назвать номер строки.
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
line := 0
for scanner.Scan() {
line++
text := strings.TrimSpace(scanner.Text())
if text == "" {
fmt.Println("строка", line, "пустая — пропускаю")
continue
}
qty, err := strconv.Atoi(text)
if err != nil {
fmt.Fprintln(os.Stderr, "Ошибка в строке", line, err)
os.Exit(1)
}
fmt.Println("строка", line, "количество", qty)
}
fmt.Println("Ввод кончился, строк:", line)
}stagiaire@lab:~/gocourse/day08/scratch$ go run . < lines.txt
строка 1 количество 12
строка 2 пустая — пропускаю
строка 3 количество 7
строка 4 количество 3
Ввод кончился, строк: 4
stagiaire@lab:~/gocourse/day08/scratch$ go run . < bad.txt
строка 1 количество 12
строка 2 количество 7
строка 3 пустая — пропускаю
Ошибка в строке 4 strconv.Atoi: parsing "3шт": invalid syntax
exit status 1Как это читать.
line++— первая строка тела: выполняется для каждой прочитанной строки, и пустой тоже, поэтому номер совпадает с номером строки в файле. Поставьте её послеcontinue— и номер в сообщении об ошибке уедет.strings.TrimSpaceиз дня 07 убирает пробелы по краям, и строка из одних пробелов становится пустой;continueпропускает остаток тела, так чтоAtoiеё не увидит.- На ошибке — сообщение в поток ошибок и
os.Exit(1), как в день 07: строка после цикла не выполнилась.exit status 1дописалgo run.
Собрать сообщение по шаблону поможет пара к Fprintln — fmt.Fprintf(os.Stderr, "…", …): то же, что Printf, только первым аргументом сказано, куда печатать.
Попробуйте сейчас: где считать номер строки.
Цель: увидеть, как место
line++в теле меняет номера строк в выводе.1. Замените программу в
scratch/main.goна эту:✎ Наберите в файлscratch/main.gopackage main import ( "bufio" "fmt" "os" "strconv" "strings" ) func main() { scanner := bufio.NewScanner(os.Stdin) line := 0 for scanner.Scan() { line++ text := strings.TrimSpace(scanner.Text()) if text == "" { fmt.Println("строка", line, "пустая — пропускаю") continue } qty, err := strconv.Atoi(text) if err != nil { fmt.Fprintln(os.Stderr, "Ошибка в строке", line, err) os.Exit(1) } fmt.Println("строка", line, "количество", qty) } fmt.Println("Ввод кончился, строк:", line) }Запустите три ввода:
▶ Выполнитеcd ~/gocourse/day08/scratch go run . < lines.txt go run . < bad.txt printf '12\n7\n' | go run .2. Перенесите
line++на строку послеif text == "" { … }и снова запустите наbad.txt:▶ Выполнитеgo run . < bad.txtДолжно получиться:
Вывод · это печатает программа, набирать не нужнострока 1 количество 12 строка 2 количество 7 строка 2 пустая — пропускаю Ошибка в строке 3 strconv.Atoi: parsing "3шт": invalid syntax exit status 13. В файле
3шт— четвёртая строка. Почему пустая названа второй, а ошибка — в строке 3? Вернитеline++первой строкой тела.Готово, когда: вывод совпал с блоком выше, вы можете объяснить, почему номера уехали, и
line++снова первой строкой тела.
Что может пойти не так
Самая коварная ошибка здесь — лишний scanner.Scan() перед циклом: он читает первую строку, Text() её никто не берёт, и строка теряется молча (код и вывод — в course extra 08).
| Что видите | Что это значит | Что делать |
|---|---|---|
| первая строка ввода не обработана | лишний scanner.Scan() до цикла |
убрать: читает только заголовок |
non-boolean condition in for statement |
написано for scanner.Scan {: без скобок это не вызов |
for scanner.Scan() { |
parsing "3шт": invalid syntax, потом exit status 1 |
в строке не число, программа вышла с кодом 1 | так и задумано |
| номер строки в сообщении меньше настоящего | счётчик стоит после continue |
line++ — первой строкой тела |
Дальше: накопители — сумма, количество и среднее по всем строкам — course next