Индекс и срез строки режут байты
Раз строка — байты, то и всё, что обращается к строке по номеру, работает с байтами. У среза s[i] — элемент номер i, у строки — байт номер i. Для латиницы разница не видна, для кириллицы она ломает текст.
s[0] — байт, а не буква
Ситуация. Отчёт группирует товары по первой букве названия. Первую букву хочется взять так же, как первый элемент среза, — name[0].
s := "Болт"
fmt.Println(s[0])
fmt.Println(s[1])
fmt.Println(s[0:2])Что увидите.
208
145
БКак это читать.
| Строка | Что происходит |
|---|---|
s[0] |
байт номер 0 — первая половина буквы Б. Тип у него byte: это число, поэтому Println печатает 208 |
s[1] |
байт номер 1 — вторая половина Б |
s[0:2] |
байты с 0 по 1 — как подсрез из дня 17, от a до b-1. Это ровно два байта буквы Б |
Номера в s[i] и s[a:b] — номера байтов: о в "Болт" — байты 2 и 3, т — 6 и 7. Последний номер — len(s) - 1.
Строку нельзя поменять по байтам: присваивание в s[0] не собирается. Новую строку собирают заново: "В" + s[2:] даёт Волт.
Попробуйте сейчас: что выведет срез строки.
Цель: предсказать, что напечатает часть строки по номерам байтов, и сдать ответ.
1. Не запуская, ответьте, что напечатает:
Код для чтения · разбираем, набирать не нужноfmt.Println("Гайка"[2:4])2. Сдайте ответ:
▶ Выполните · выделенное замените своимcourse answer day19.q2 ОТВЕТВместо
ОТВЕТ— то, что появится на экране. Если это буквы, наберите их в той раскладке, в какой они напечатаны.3. Проверьте запуском в
scratch.Готово, когда:
course answerответилПринято: day19.q2 = …(пункт проверкиq2).
Буква, разрезанная пополам
Номера в s[a:b] можно взять любые — Go не следит, чтобы разрез попал между буквами.
s := "Болт"
fmt.Println(s[:3])
fmt.Println(s[:1] + "!")Что увидите. Так это выглядит в терминале:
stagiaire@lab:~/gocourse/day19/scratch$ go run .
Б�
�!Как это читать. s[:3] — байты 0, 1 и 2: целая Б и половина о; s[:1] — половина Б. Программа не упала и честно напечатала эти байты, а терминал на месте половины буквы рисует знак замены �.
Что напечатано на самом деле, покажет od — байты числами: -tu1 — по одному байту, -An — без номеров слева.
stagiaire@lab:~/gocourse/day19/scratch$ go run . | od -An -tu1
208 145 208 10 208 33 10208 145 — Б, одинокое 208 — половина о, 10 — перевод строки, 33 — !. Ромба в выводе нет: его дорисовал терминал.
В отчёте это выглядит так: название обрезали name[:12], и у половины товаров в конце колонки �. На латинских названиях всё правильно — поэтому проверять такие места нужно на русских.
Попробуйте сейчас: байт вместо буквы.
Цель: своими глазами увидеть байт
208и знак�от разрезанной буквы.1. Наберите и запустите:
✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { s := "Болт" fmt.Println(s[0]) fmt.Println(s[1]) fmt.Println(s[0:2]) }▶ Выполнитеcd ~/gocourse/day19/scratch go run .2. Замените три строки печати на две — с разрезом посреди буквы:
✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { s := "Болт" fmt.Println(s[:3]) fmt.Println(s[:1] + "!") }▶ Выполнитеgo run . go run . | od -An -tu1Готово, когда: первая программа напечатала
208,145иБ, а вторая — строки с�;odпоказал среди чисел одинокое208. Проверка дня этот опыт не сверяет.
Что может пойти не так
Номера строк в сообщениях — для фрагментов, вставленных в scratch: первая строка тела main — шестая.
Индекс за концом строки, записанной прямо в коде, компилятор видит сам:
stagiaire@lab:~/gocourse/day19/scratch$ go run .
# day19/scratch
./main.go:6:25: invalid argument: index 8 out of bounds [0:8]Это fmt.Println("Болт"[8]): байты у "Болт" — с 0 по 7. Строку в переменной компилятор не проверяет, и программа падает при запуске — s := "Болт" и fmt.Println(s[8]):
stagiaire@lab:~/gocourse/day19/scratch$ go run .
panic: runtime error: index out of range [8] with length 8
goroutine 1 [running]:
main.main()
/home/stagiaire/gocourse/day19/scratch/main.go:7 +0x9
exit status 2| Что видите | Что это значит | Что делать |
|---|---|---|
invalid argument: index 8 out of bounds [0:8] |
индекс строки-константы за концом: [0:8] — допустимые номера с 0 до 7 |
номер от 0 до len(s) - 1, и помнить, что это байты |
panic: runtime error: index out of range [8] with length 8 |
то же для строки в переменной; length 8 — длина в байтах |
то же |
cannot assign to s[0] (neither addressable nor a map index expression) |
попытка поменять байт строки: строки в Go не меняются | собрать новую строку: "В" + s[2:] |
invalid operation: s[0] == "Б" (mismatched types byte and untyped string) |
байт сравнили со строкой | проверять начало строки через strings.HasPrefix — четвёртая страница |
в выводе � вместо буквы |
строку разрезали посреди буквы | резать по буквам — следующая страница |
печатается число вроде 208 вместо буквы |
напечатали s[i] — байт |
буквы — следующая страница |
Дальше: как пройти строку по буквам — course next