web-dev-qa-db-fra.com

Comment obtenir le nombre de caractères dans une chaîne?

Comment puis-je obtenir le nombre de caractères d'une chaîne dans Go?

Par exemple, si j'ai une chaîne "hello", la méthode devrait renvoyer 5. J'ai vu que len(str) renvoie le nombre d'octets et non le nombre de caractères, donc len("£") renvoie 2 au lieu de 1 car £ est codé avec deux octets en UTF-8.

104
Ammar

Vous pouvez essayer RuneCountInString à partir du paquet utf8.

renvoie le nombre de runes dans p

comme illustré dans ce script : la longueur de "Monde" pourrait être 6 (lorsqu'il est écrit en chinois: "世界"), mais son nombre de runes est 2:

package main

import "fmt"
import "unicode/utf8"

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

Phrozen ajoute dans les commentaires :

En fait, vous pouvez utiliser len() sur les runes en tapant simplement le casting.
len([]rune("世界")) imprimera 2. Aux départs en Go 1.3.


Et avec CL 108985 _ (mai 2018, pour Go 1.11), len([]rune(string)) est maintenant optimisé. (Corrige numéro 24923 )

Le compilateur détecte automatiquement le modèle len([]rune(string)) et le remplace par pour l’appel de r: = range s.

Ajoute une nouvelle fonction d'exécution pour compter les runes dans une chaîne . Modifie le compilateur pour détecter le motif len([]rune(string)) et le remplace par la nouvelle fonction d'exécution du comptage des runes.

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 _ pointe vers l'article de blog " Normalisation du texte sur Go "

Qu'est-ce qu'un personnage?

Comme mentionné dans le message de blog sur les chaînes de caractères , les caractères peuvent s'étendre sur plusieurs runes.
Par exemple, un 'e' et '' (aiguë "\ u0301") peuvent être combinés pour former un "é" ("e\u0301" dans NFD). Ensemble, ces deux runes ne forment qu'un seul personnage

La définition d'un caractère peut varier en fonction de l'application.
Pour NORMALISATION, nous le définirons comme suit:

  • une séquence de runes qui commence par un démarreur, 
  • une rune qui ne modifie ni ne combine en arrière avec aucune autre rune, 
  • suivi par une séquence éventuellement vide de non-partants, c'est-à-dire de runes qui le font (généralement des accents). 

L'algorithme de normalisation traite un caractère à la fois. 

En utilisant ce paquetage et son Iter type _, le nombre réel de "caractère" serait:

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)
}

Ici, ceci utilise le formulaire de normalisation Unicode NFKD "Compatibility Decomposition"

128
VonC

Il existe un moyen d'obtenir le nombre de runes sans paquet en convertissant une chaîne en [] rune en tant que 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)))

}

nombre d'octets 30 16

nombre de runes 16 16

32
Denis Kreshikhin

Cela dépend beaucoup de votre définition de ce qu'est un "personnage". Si "rune égale un personnage" convient à votre tâche (généralement, ce n'est pas le cas), la réponse de VonC est parfaite pour vous. Sinon, il faut probablement noter qu'il y a peu de situations où le nombre de runes dans une chaîne Unicode est une valeur intéressante. Et même dans ces situations, il est préférable, si possible, de déduire le nombre en "parcourant" la chaîne lors du traitement des runes pour éviter de doubler l'effort de décodage UTF-8.

5
zzzz

Si vous devez prendre en compte les grappes de graphèmes, utilisez le module regexp ou unicode. Compter le nombre de points de code (runes) ou d'octets est également nécessaire pour la validation car la longueur du cluster de graphèmes est illimitée. Si vous souhaitez éliminer des séquences extrêmement longues, vérifiez si elles sont conformes au format de texte stream-safe .

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) 
}
4
masakielastic

Je tiens à souligner qu'aucune des réponses fournies jusqu'à présent ne vous donne le nombre de caractères que vous attendez, en particulier lorsque vous avez affaire à des emojis (mais aussi à certaines langues comme le thaï, le coréen ou l'arabe). Suggestions de VonC produira les informations suivantes:

fmt.Println(utf8.RuneCountInString("????️‍????????????")) // Outputs "6".
fmt.Println(len([]rune("????️‍????????????"))) // Outputs "6".

En effet, ces méthodes ne comptent que les points de code Unicode. De nombreux caractères peuvent être composés de plusieurs points de code.

Pareil pour utiliser le paquet de normalisation :

var ia norm.Iter
ia.InitString(norm.NFKD, "????️‍????????????")
nc := 0
for !ia.Done() {
    nc = nc + 1
    ia.Next()
}
fmt.Println(nc) // Outputs "6".

La normalisation n'est pas vraiment la même chose que compter les caractères et de nombreux caractères ne peuvent pas être normalisés en un équivalent d'un point de code.

la réponse de masakielastic s'approche mais ne gère que les modificateurs (le drapeau Rainbow contient un modificateur qui n'est donc pas compté comme son propre point de code):

fmt.Println(GraphemeCountInString("????️‍????????????"))  // Outputs "5".
fmt.Println(GraphemeCountInString2("????️‍????????????")) // Outputs "5".

La manière correcte de scinder les chaînes Unicode en caractères (perçus par l'utilisateur), c'est-à-dire des grappes de graphèmes, est définie dans le Unicode Standard Annex # 29 . Les règles peuvent être trouvées dans Section 3.1.1 . Le package github.com/rivo/uniseg implémente ces règles afin que vous puissiez déterminer le nombre correct de caractères dans une chaîne:

fmt.Println(uniseg.GraphemeClusterCount("????️‍????????????")) // Outputs "2".
3
Oliver

Il y a plusieurs façons d'obtenir une longueur de chaîne:

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)

}

1
pigletfly

J'ai essayé de rendre la normalisation un peu plus rapide:

    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
    }
0
Marcelloh