Runes and Bytes
A Go string is a read-only slice of bytes, and its bytes are UTF-8. That one sentence explains most of what surprises people about strings in Go.
len(s) counts bytes. Indexing, s[0], gives you a single byte. Neither one
knows anything about characters, so for "héllo" you get 6 and the first
byte of h, and for "日本語" you get 9.
A rune is a Unicode code point: the thing you probably mean when you say
"character". It is an alias for int32. Two operations decode UTF-8 for you:
for i, r := range s { ... } // i is a BYTE offset, r is a rune
runes := []rune(s) // decodes the whole string at once
Ranging over a string steps one rune at a time, but the index it hands you is where that rune starts in bytes, not which rune it is. That gap is where the bugs live.
Task
Finish the four functions in main.go.
ByteLen(s string) int- how many bytes.RuneLen(s string) int- how many runes.RuneAt(s string, i int) (rune, bool)- the rune at positionicounted in runes.RuneAt("héllo", 1)is'é', not the second byte ofé. Out of range, including a negativei, returnsfalse.ReverseRunes(s string) string- reverse the string without cutting a multi-byte rune in half."héllo"reverses to"olléh", and reversing twice must give the original back.
Hints
-
for range swith no variables at all is legal and is allRuneLenneeds.utf8.RuneCountInString(s)fromunicode/utf8is the standard-library way to say the same thing. -
In
RuneAt, ignore the indexrangegives you and keep your own counter, or convert with[]rune(s)and index that. The byte offset is not the rune position: in"héllo"thelafteréstarts at byte 3. -
Reversing bytes is the classic wrong answer.
s[len(s)-1-i]walks backwards through bytes, and the two bytes ofécome out swapped, which is no longer valid UTF-8. Convert to[]runefirst. -
Swapping two elements needs no temporary variable:
runes[i], runes[j] = runes[j], runes[i]and a
forcan advance two things at once withfor i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1. -
string(runes)converts a[]runeback to a string.string(r)on a single rune works too, but note thatstring(65)is"A"and not"65": turning a number into its digits isstrconv, which you will meet later.