Как получить количество символов в строке?
Как я могу получить количество символов строки в Go?
Например, если у меня есть строка "hello"
метод должен вернуться 5
, я видел это len(str)
возвращает количество байтов, а не количество символов, так len("£")
возвращает 2 вместо 1, потому что £ кодируется двумя байтами в UTF-8.
4 ответа
Ты можешь попробовать RuneCountInString
из пакета utf8.
возвращает количество рун в р
что, как показано в этом сценарии: длина "Мира" может быть 6 (когда написано по-китайски: "世界"), но число рун равно 2:
package main
import "fmt"
import "unicode/utf8"
func main() {
fmt.Println("Hello, 世界", len("世界"), utf8.RuneCountInString("世界"))
}
Phrozen добавляет в комментарии:
На самом деле вы можете сделатьlen()
над рунами, просто набрав тип.len([]rune("世界"))
распечатает2
, По латы в Go 1.3.
И с CL 108985(май 2018, для Go 1.11), len([]rune(string))
сейчас оптимизирован. ( Исправляет проблему 24923)
Компилятор обнаруживаетlen([]rune(string))
автоматически и заменяет его на вызов r:= range s.
Добавляет новую функцию времени выполнения для подсчета рун в строке. Изменяет компилятор для определения шаблона
len([]rune(string))
и заменяет его новой функцией подсчета времени выполнения рун.
RuneCount/lenruneslice/ASCII 27.8ns ± 2% 14.5ns ± 3% -47.70% (p=0.000 n=10+10)
RuneCount/lenruneslice/Japanese 126ns ± 2% 60ns ± 2% -52.03% (p=0.000 n=10+10)
RuneCount/lenruneslice/MixedLength 104ns ± 2% 50ns ± 1% -51.71% (p=0.000 n=10+9)
Stefan Steiger указывает на сообщение в блоге " Нормализация текста в Go "
Что такое персонаж?
Как было упомянуто в сообщении в блоге о строках, персонажи могут охватывать несколько рун
Например,e
'и'◌́◌́' (острый "\u0301") могут объединиться в "é" ("e\u0301
"в НФД). Вместе эти две руны являются одним персонажем.Определение символа может варьироваться в зависимости от приложения.
Для нормализации мы определим это как:
- последовательность рун, которая начинается со стартера,
- руна, которая не изменяет и не комбинирует в обратном направлении с любой другой руной,
- сопровождаемый возможно пустой последовательностью не начальных символов, то есть рун, которые делают (обычно акценты).
Алгоритм нормализации обрабатывает один символ за раз.
Используя этот пакет и его Iter
тип, фактическое число "символов" будет:
package main
import "fmt"
import "golang.org/x/text/unicode/norm"
func main() {
var ia norm.Iter
ia.InitString(norm.NFKD, "école")
nc := 0
for !ia.Done() {
nc = nc + 1
ia.Next()
}
fmt.Printf("Number of chars: %d\n", nc)
}
Здесь используется форма нормализации Unicode NFKD "Декомпозиция совместимости"
Существует способ получить количество рун без каких-либо пакетов путем преобразования строки в []rune как len([]rune(YOUR_STRING))
:
package main
import "fmt"
func main() {
russian := "Спутник и погром"
english := "Sputnik & pogrom"
fmt.Println("count of bytes:",
len(russian),
len(english))
fmt.Println("count of runes:",
len([]rune(russian)),
len([]rune(english)))
}
количество байтов 30 16
количество рун 16 16
Я должен отметить, что ни один из приведенных ответов не дает вам того количества символов, которое вы ожидаете, особенно когда вы имеете дело с эмодзи (но также и с некоторыми языками, такими как тайский, корейский или арабский). Предложения VonC выведут следующее:
fmt.Println(utf8.RuneCountInString("️")) // Outputs "6".
fmt.Println(len([]rune("️"))) // Outputs "6".
Это потому, что эти методы только подсчитывают кодовые точки Unicode. Есть много символов, которые могут состоять из нескольких кодов.
То же самое для использования пакета нормализации:
var ia norm.Iter
ia.InitString(norm.NFKD, "️")
nc := 0
for !ia.Done() {
nc = nc + 1
ia.Next()
}
fmt.Println(nc) // Outputs "6".
Нормализация на самом деле не то же самое, что подсчет символов, и многие символы не могут быть нормализованы в эквивалент одной кодовой точки.
Ответ masakielastic подходит близко, но обрабатывает только модификаторы (радужный флаг содержит модификатор, который, таким образом, не считается его собственной кодовой точкой):
fmt.Println(GraphemeCountInString("️")) // Outputs "5".
fmt.Println(GraphemeCountInString2("️")) // Outputs "5".
Правильный способ разделения строк Unicode на (воспринимаемые пользователем) символы, то есть кластеры графем, определен в Стандартном приложении Unicode № 29. Правила можно найти в разделе 3.1.1. Пакет https://github.com/rivo/uniseg реализует эти правила, чтобы вы могли определить правильное количество символов в строке:
fmt.Println(uniseg.GraphemeClusterCount("️")) // Outputs "2".
Есть несколько способов получить длину строки:
package main
import (
"bytes"
"fmt"
"strings"
"unicode/utf8"
)
func main() {
b := "这是个测试"
len1 := len([]rune(b))
len2 := bytes.Count([]byte(b), nil) -1
len3 := strings.Count(b, "") - 1
len4 := utf8.RuneCountInString(b)
fmt.Println(len1)
fmt.Println(len2)
fmt.Println(len3)
fmt.Println(len4)
}
Если вам нужно принять во внимание кластеры графем, используйте модуль регулярных выражений или кодировку Юникод. Подсчет количества кодовых точек (рун) или байтов также необходим для проверки, так как длина кластера графем не ограничена. Если вы хотите исключить очень длинные последовательности, проверьте, соответствуют ли последовательности потокобезопасному текстовому формату.
package main
import (
"regexp"
"unicode"
"strings"
)
func main() {
str := "\u0308" + "a\u0308" + "o\u0308" + "u\u0308"
str2 := "a" + strings.Repeat("\u0308", 1000)
println(4 == GraphemeCountInString(str))
println(4 == GraphemeCountInString2(str))
println(1 == GraphemeCountInString(str2))
println(1 == GraphemeCountInString2(str2))
println(true == IsStreamSafeString(str))
println(false == IsStreamSafeString(str2))
}
func GraphemeCountInString(str string) int {
re := regexp.MustCompile("\\PM\\pM*|.")
return len(re.FindAllString(str, -1))
}
func GraphemeCountInString2(str string) int {
length := 0
checked := false
index := 0
for _, c := range str {
if !unicode.Is(unicode.M, c) {
length++
if checked == false {
checked = true
}
} else if checked == false {
length++
}
index++
}
return length
}
func IsStreamSafeString(str string) bool {
re := regexp.MustCompile("\\PM\\pM{30,}")
return !re.MatchString(str)
}
Многое зависит от вашего определения, что такое "характер". Если "руна равна персонажу" - это нормально для вашей задачи (как правило, это не так), тогда ответ от VonC идеально подходит для вас. В противном случае, вероятно, следует отметить, что существует несколько ситуаций, когда количество рун в строке Unicode является интересным значением. И даже в таких ситуациях лучше, если это возможно, вывести счетчик при "прохождении" строки во время обработки рун, чтобы избежать удвоения усилий по декодированию UTF-8.
Я попытался сделать нормализацию немного быстрее:
en, _ = glyphSmart(data)
func glyphSmart(text string) (int, int) {
gc := 0
dummy := 0
for ind, _ := range text {
gc++
dummy = ind
}
dummy = 0
return gc, dummy
}