Вопрос

Как я могу получить количество символов строки в Go?

Например, если у меня есть строка "hello" метод должен вернуть 5.я видел это len(str) возвращает количество байтов и не количество символов так len("£") возвращает 2 вместо 1, поскольку £ закодирован двумя байтами в UTF-8.

Это было полезно?

Решение

Можешь попробовать RuneCountInString из пакета utf8.

возвращает количество рун в p

что, как показано в этот сценарий:длина «World» может быть равна 6 (при написании на китайском языке:«世界»), но количество рун равно 2:

package main

import "fmt"
import "unicode/utf8"

func main() {
    fmt.Println("Hello, 世界", len("世界"), utf8.RuneCountInString("世界"))
}

Фрозен добавляет в комментариях:

На самом деле вы можете сделать len() над рунами, просто набрав текст.
len([]rune("世界")) напечатаю 2.В Go 1.3.


И с КЛ 108985 (май 2018 г., для Go 1.11), len([]rune(string)) теперь оптимизирован.(Исправления выпуск 24923)

Компилятор обнаруживает len([]rune(string)) шаблон автоматически и заменяет его вызовом for r := range .

Добавляет новую функцию времени выполнения для подсчета рун в строке.Изменяет компилятор для обнаружения шаблона 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)

Стефан Штайгер указывает на сообщение в блоге "Нормализация текста в 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)
}

Здесь для этого используется Форма нормализации Юникода НФКД «Разложение совместимости»


Оливер's отвечать указывает на СЕГМЕНТАЦИЯ ТЕКСТА ЮНИКОДА как единственный способ надежно определить границы по умолчанию между определенными значимыми текстовыми элементами:воспринимаемые пользователем символы, слова и предложения.

Для этого вам понадобится внешняя библиотека, например риво/унисег, что делает Сегментация текста Юникода.

Это действительно будет иметь значение»графема кластер", где несколько кодовых точек могут быть объединены в один воспринимаемый пользователем символ.

package uniseg

import (
    "fmt"

    "github.com/rivo/uniseg"
)

func main() {
    gr := uniseg.NewGraphemes("👍🏼!")
    for gr.Next() {
        fmt.Printf("%x ", gr.Runes())
    }
    // Output: [1f44d 1f3fc] [21]
}

Две графемы, хотя рун три (кодовые точки Юникода).

Другие советы

Существует способ получить количество рун без каких-либо пакетов, преобразовав строку в []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 идеально подходит для вас.В противном случае, вероятно, следует отметить, что существует несколько ситуаций, когда количество рун в строке Unicode является интересным значением.И даже в таких ситуациях лучше, если возможно, вывести счетчик во время «обхода» строки по мере обработки рун, чтобы избежать удвоения усилий по декодированию UTF-8.

Если вам нужно принять во внимание кластеры графем, используйте регулярное выражение или модуль unicode.Подсчет количества кодовых точек (рун) или байтов также необходим для проверки, поскольку длина кластера графем не ограничена.Если вы хотите исключить очень длинные последовательности, проверьте, соответствуют ли последовательности потокобезопасный текстовый формат.

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 выведет следующее:

fmt.Println(utf8.RuneCountInString("🏳️‍🌈🇩🇪")) // Outputs "6".
fmt.Println(len([]rune("🏳️‍🌈🇩🇪"))) // Outputs "6".

Это связано с тем, что эти методы учитывают только кодовые точки Юникода.Существует множество символов, которые могут состоять из нескольких кодовых точек.

То же самое для использования Пакет нормализации:

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".

Правильный способ разделения строк Юникода на (воспринимаемые пользователем) символы, т.е.кластеры графем, определены в Стандартное приложение Юникод № 29.Правила можно найти в Раздел 3.1.1github.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)

}

Я попытался сделать нормализацию немного быстрее:

    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
    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top