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

Индекс и срез строки режут байты

Раз строка — байты, то и всё, что обращается к строке по номеру, работает с байтами. У среза 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  10

208 145 — Б, одинокое 208 — половина о, 10 — перевод строки, 33 — !. Ромба в выводе нет: его дорисовал терминал.

В отчёте это выглядит так: название обрезали name[:12], и у половины товаров в конце колонки �. На латинских названиях всё правильно — поэтому проверять такие места нужно на русских.


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

Цель: своими глазами увидеть байт 208 и знак � от разрезанной буквы.

1. Наберите и запустите:

✎ Наберите в файл scratch/main.go
package 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.go
package 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