Я написал следующую Reverse
функцию, которая учитывает кодировку UTF8 и комбинированные символы:
// Reverse reverses the input while respecting UTF8 encoding and combined characters
func Reverse(text string) string {
textRunes := []rune(text)
textRunesLength := len(textRunes)
if textRunesLength <= 1 {
return text
}
i, j := 0, 0
for i < textRunesLength && j < textRunesLength {
j = i + 1
for j < textRunesLength && isMark(textRunes[j]) {
j++
}
if isMark(textRunes[j-1]) {
// Reverses Combined Characters
reverse(textRunes[i:j], j-i)
}
i = j
}
// Reverses the entire array
reverse(textRunes, textRunesLength)
return string(textRunes)
}
func reverse(runes []rune, length int) {
for i, j := 0, length-1; i < length/2; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
}
// isMark determines whether the rune is a marker
func isMark(r rune) bool {
return unicode.Is(unicode.Mn, r) || unicode.Is(unicode.Me, r) || unicode.Is(unicode.Mc, r)
}
Я приложил все усилия, чтобы сделать его максимально эффективным и читабельным. Идея проста: пройти через руны в поисках комбинированных символов, а затем поменять руны комбинированных символов на месте. После того как мы рассмотрим их все, поменяйте местами руны всей строки.
Скажем, мы хотели бы перевернуть эту строку bròwn
. ò
представлен двумя рунами, одна для o
и одна для этого юникода \u0301a
, представляющего "могилу".
Для простоты давайте представим строку, подобную этой bro'wn
. Первое, что мы делаем, это ищем комбинированные символы и обращаем их вспять. Итак, теперь у нас есть строка br'own
. Наконец, мы переворачиваем всю строку и в итоге получаем nwo'rb
. Это возвращается нам как nwòrb
Вы можете найти его здесь https://github.com/shomali11/util, если хотите его использовать.
Вот несколько тестов, демонстрирующих несколько различных сценариев:
func TestReverse(t *testing.T) {
assert.Equal(t, Reverse(""), "")
assert.Equal(t, Reverse("X"), "X")
assert.Equal(t, Reverse("b\u0301"), "b\u0301")
assert.Equal(t, Reverse("?⚽"), "⚽?")
assert.Equal(t, Reverse("Les Mise\u0301rables"), "selbare\u0301siM seL")
assert.Equal(t, Reverse("ab\u0301cde"), "edcb\u0301a")
assert.Equal(t, Reverse("This `\xc5` is an invalid UTF8 character"), "retcarahc 8FTU dilavni na si `�` sihT")
assert.Equal(t, Reverse("The quick bròwn 狐 jumped over the lazy 犬"), "犬 yzal eht revo depmuj 狐 nwòrb kciuq ehT")
}