Строка — это байты
Выгрузка заказов со склада — текст: русские названия, числа, точки с запятой. Сегодня программа прочитает его из файла и напечатает ровный отчёт. Первая ловушка — длина строки: len считает не буквы.
Колонка разъехалась
Ситуация. Названия товаров нужно напечатать колонкой: короткое название дополнить точками до двенадцати знаков и поставить черту. Длину названия берём через len — тем же len, которым в день 16 мерили срез.
package main
import (
"fmt"
"strings"
)
func main() {
width := 12
names := []string{"Bolt-M8", "Болт М8", "Шайба"}
for _, name := range names {
dots := strings.Repeat(".", width-len(name))
fmt.Println(name + dots + "|")
}
}strings.Repeat(".", n) возвращает строку из n точек.
Что увидите.
stagiaire@lab:~/gocourse/day19/scratch$ go run .
Bolt-M8.....|
Болт М8|
Шайба..|Как это читать. У латинского Bolt-M8 семь знаков и пять точек — черта на месте. У русского Болт М8 знаков тоже семь, а точек ни одной, у Шайба пять букв и две точки. Ошибка не в арифметике: len вернул не то число, которого мы ждали.
len считает байты
Дальше показываем только строки из тела main — вставляйте их между { и } в scratch/main.go. С одним import "fmt" первая строка тела — шестая.
fmt.Println(len("Bolt"))
fmt.Println(len("Болт"))
fmt.Println([]byte("Bolt"))
fmt.Println([]byte("Болт"))Что увидите.
4
8
[66 111 108 116]
[208 145 208 190 208 187 209 130]Как это читать. Компьютер хранит текст числами. Одно такое число — байт: от 0 до 255. []byte("Болт") показывает, из каких байтов состоит строка: это срез байтов, как срез чисел из дня 16.
Латинской букве хватает одного байта: B — 66, o — 111. Русских букв в 256 значений не уместить, поэтому Go записывает строки в кодировке UTF-8: латиница — один байт, русская буква — два. Б — это пара 208 145, о — пара 208 190. Восемь байтов, четыре буквы.
len у строки — число байтов, не букв. Для латиницы эти числа совпадают, поэтому ошибка годами живёт в программах, проверенных на английских названиях.
Сколько байтов у знаков, которые встречаются в выгрузках:
fmt.Println(len("ё"), len("№"), len("€"), len(" "))2 3 3 1| Знак | Байтов |
|---|---|
латинская буква, цифра, пробел, ;, . |
1 |
русская буква, в том числе ё |
2 |
№, € |
3 |
Теперь колонка из первого раздела объясняется: в Болт М8 пять русских букв по два байта и два знака по одному (пробел и 8) — len вернул 12, и на точки осталось 12 - 12 = 0.
Попробуйте сейчас: байты и буквы.
Цель: увидеть, что у одной и той же строки байтов больше, чем букв, — в программе и в терминале.
1. Наберите программу и запустите:
✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { fmt.Println(len("Bolt")) fmt.Println(len("Болт")) fmt.Println([]byte("Bolt")) fmt.Println([]byte("Болт")) }▶ Выполнитеcd ~/gocourse/day19/scratch go run .2. Терминал умеет считать то же самое.
wc -cсчитает байты,wc -m— знаки:▶ Выполнитеprintf 'Болт' | wc -c printf 'Болт' | wc -mЧто должно получиться:
Вывод · это печатает программа, набирать не нужно8 4Готово, когда: программа напечатала
4и8, аwc—8и4(пункт проверкиt_bytes).
Попробуйте сейчас: что выведет len.
Цель: предсказать длину строки с русскими буквами и сдать ответ.
1. Не запуская, посчитайте, что напечатает строка из тела
main:Код для чтения · разбираем, набирать не нужноfmt.Println(len("Гайка М10"))Буква
Мздесь русская.2. Сдайте число:
▶ Выполните · выделенное замените своимcourse answer day19.q1 ЧИСЛОВместо
ЧИСЛО— то, что напечатает программа.3. Проверьте запуском в
scratch.Готово, когда:
course answerответилПринято: day19.q1 = …(пункт проверкиq1).
Что может пойти не так
Добавим в колонку из первого раздела длинное русское название — "Анкер клиновой", четырнадцать букв:
stagiaire@lab:~/gocourse/day19/scratch$ go run .
Bolt-M8.....|
Болт М8|
Шайба..|
panic: strings: negative Repeat count
goroutine 1 [running]:
strings.Repeat({0x49b000?, 0x1fccb4922008?}, 0x1fccb4876e58?)
/usr/local/go/src/strings/strings.go:609 +0x56b
main.main()
/home/stagiaire/gocourse/day19/scratch/main.go:12 +0xbc
exit status 2В "Анкер клиновой" 27 байтов, 12 - 27 меньше нуля, а строку из минус пятнадцати точек strings.Repeat сделать не может — программа падает. Строка 12 — та, где считаются точки.
| Что видите | Что это значит | Что делать |
|---|---|---|
panic: strings: negative Repeat count |
ширину дополнения посчитали через len, а байтов в названии больше ширины |
считать буквы, а не байты — третья страница |
| колонка ровная у латинских названий и разъезжается у русских | длина взята через len |
то же |
len строки больше, чем букв в ней |
так и есть: русская буква — два байта | для длины в буквах — третья страница |
wc -c и wc -m печатают разные числа |
-c — байты, -m — знаки |
ничего, так и должно быть |
Дальше: индекс строки — это номер байта, а не буквы — course next