Руна против байта, начиная со строки - PullRequest
1 голос
/ 31 октября 2019

Согласно https://blog.golang.org/strings и моим тестам, похоже, что пока мы range строка, символы, которые мы получаем, имеют тип rune, но если мы получим ее str[index], они будут byte типа, почему?

Ответы [ 2 ]

2 голосов
/ 31 октября 2019

На первом уровне, , почему , потому что это , как определяется язык . Тип String говорит нам, что:

Строковое значение представляет собой (возможно, пустую) последовательность байтов. Количество байтов называется длиной строки и никогда не бывает отрицательным. Строки являются неизменяемыми: после создания невозможно изменить содержимое строки.

и:

Байты строки могут быть доступны с помощью целочисленных индексов от 0 до len(s) -1.

Между тем, range - это предложение, которое можно вставить в оператор for , а в спецификации сказано:

Выражение справа в предложении "range" называется выражением диапазона , которое может быть ... [a] string ...

и:

Для строкового значения предложение «range» выполняет итерации по кодовым точкам Unicode в строке, начиная с байтового индекса 0. В последовательных итерациях значение индекса будет индексом первого байта последовательного кодированного UTF-8кодовые точки в строке, а второе значение типа rune будет значением соответствующей кодовой точки. Если итерация встречает недопустимую последовательность UTF-8, второе значение будет 0xFFFD, символ замены Unicode, и следующая итерация будет продвигать один байт в строке.

Если вы хотите знать, почему язык определяется таким образом, вам действительно нужно спросить самих определителей. Тем не менее, обратите внимание, что если for ранжируется только по байтам, вам нужно создать свои собственные причудливые циклы, чтобы охватить руны. Учитывая, что for ... range работает через руны, если вы хотите работать с байтами в строке s, вы можете написать:

for i := 0; i < len(s); i++ {
    ...
}

и легкий доступ s[i] внутри петли. Вы также можете написать:

for i, b := range []byte(s) {
}

и получить доступ как к индексу i, так и к байту b внутри цикла. (Преобразование из строки в []byte или наоборот может потребовать копию, поскольку []byte может быть изменено. В этом случае, однако, range не изменяет его, и компилятор может оптимизировать копию. См. комментарий icza ниже или этот ответ до golang: [] байт (строка) против [] байт (* строка) .) Таким образом, вы не потеряли ни одной способности,просто возможно smidgen краткости.

1 голос
/ 31 октября 2019

Просто быстрый и простой ответ о том, почему язык определяется таким образом.

Подумайте, что такое руна. rune представляет кодовую точку Unicode, которая может состоять из нескольких байтов, а также иметь различные представления в зависимости от кодировки.

Теперь подумайте, что будет означать выполнение mystring[i], если оно вернет rune ине byte. Поскольку вы не можете узнать длину каждой руны без сканирования строки, эта операция потребует сканирования всей строки каждый раз, поэтому для доступа в виде массива потребуется O (n) вместо O (1).

Для пользователей языка было бы очень нелогичным, если бы mystring[i] каждый раз сканировал всю строку, а также было бы сложнее для разработчиков языка. Вот почему большинство языков программирования (таких как Go, Rust, Python), которые различают символы Unicode и байты, возвращают только байты при индексации.

Доступ к строке по одному rune за раз вместо этого намного проще при итерации с начала, как, например, с использованием range. Последовательные байты можно сканировать и группировать вместе, пока они не сформируют действительный символ Unicode, который можно вернуть как rune, переходя к следующему.

...