Swift countElements () renvoie une valeur incorrecte lors du comptage des emoji

let str1 = "🇩🇪🇩🇪🇩🇪🇩🇪🇩🇪" let str2 = "🇩🇪.🇩🇪.🇩🇪.🇩🇪.🇩🇪." println("\(countElements(str1)), \(countElements(str2))") 

Résultat: 1, 10

Mais ne devrait pas str1 avoir 5 éléments?

Le bug semble seulement survenu quand j'utilise le drapeau emoji.

    Mise à jour pour Swift 4 (Xcode 9)

    À partir de Swift 4 (testé avec Xcode 9 beta), les grappes de graphèmes se cassent après chaque deuxième symbole d'indicateur régional, comme l'exige la norme Unicode 9:

     let str1 = "🇩🇪🇩🇪🇩🇪🇩🇪🇩🇪" print(str1.count) // 5 print(Array(str1)) // ["🇩🇪", "🇩🇪", "🇩🇪", "🇩🇪", "🇩🇪"] 

    Aussi Ssortingng est une collection de ses caractères (encore), donc on peut get le nombre de caractères avec str1.count .


    (Ancienne réponse pour Swift 3 et plus vieux 🙂

    A partir de "3 limites de grappes de graphèmes" dans "l'Annexe type n ° 29 SEGMENTATION DE TEXTE UNICODE": (pas d'italique dans l'original):

    Un cluster de graphèmes hérités est défini comme une base (telle que A ou カ) suivie de zéro ou plusieurs caractères continus. Une façon de penser à ceci est comme une séquence de caractères qui forment une "stack".

    La base peut être constituée de caractères uniques ou être n'importe quelle séquence de caractères Hangul Jamo formant une syllabe Hangul, telle que définie par D133 dans la norme Unicode, ou être n'importe quelle séquence de caractères RegionalIIndicator (RI) . Les caractères RI sont utilisés par paires pour désigner les symboles du drapeau national Emoji correspondant aux codes de pays ISO. Les séquences de plus de deux caractères RI doivent être séparées par d'autres caractères , tels que U + 200B ZWSP.

    (Merci à @rintaro pour le lien).

    Un caractère Swift représente un cluster de graphèmes étendu, donc il est (selon cette reference) correct que toute séquence de symboles indicateurs régionaux est comptée comme un seul caractère.

    Vous pouvez séparer les "drapeaux" par une ZERO WIDTH NON-JOINER:

     let str1 = "🇩🇪\u{200C}🇩🇪" print(str1.characters.count) // 2 

    ou insérez un ESPACE ZÉRO LARGE:

     let str2 = "🇩🇪\u{200B}🇩🇪" print(str2.characters.count) // 3 

    Cela résout également les ambiguïtés possibles, par exemple "🇫 🇷 🇺 🇸" doit-il être "🇫 🇷🇺 🇸" ou "🇫🇷 🇺🇸"?

    Voir aussi Comment savoir si deux emojis seront affichés comme un emoji? à propos d'une méthode possible pour countr le nombre de "caractères composés" dans une string Swift, qui returnnerait 5 pour votre let str1 = "🇩🇪🇩🇪🇩🇪🇩🇪🇩🇪" .

    Voici comment j'ai résolu ce problème, pour Swift 3 :

     let str = "🇩🇪🇩🇪🇩🇪🇩🇪🇩🇪" //or whatever the ssortingng of emojis is let range = str.startIndex..<str.endIndex var length = 0 str.enumerateSubssortingngs(in: range, options: NSSsortingng.EnumerationOptions.byComposedCharacterSequences) { (subssortingng, subssortingngRange, enclosingRange, stop) -> () in length = length + 1 } print("Character Count: \(length)") 

    Cela corrige tous les problèmes avec le nombre de caractères et emojis, et est la méthode la plus simple que j'ai trouvé.

    Comme mentionné dans la documentation :

    Notez également que le nombre de caractères renvoyé par countElements n'est pas toujours identique à la propriété length d'un NSSsortingng qui contient les mêmes caractères. La longueur d'un NSSsortingng est basée sur le nombre d'unités de code de 16 bits dans la représentation UTF-16 de la string et non sur le nombre de clusters de graphèmes étendus Unicode dans la string . Pour refléter ce fait, la propriété length de NSSsortingng s'appelle utf16Count lorsqu'elle est accédée sur une valeur de string Swift.

    Ce que vous obtenez en utilisant CountElements est le nombre de grappes de graphèmes étendus Unicode. Puisque str1 a tous les mêmes caractères emoji, CountElements renvoie 1. Si vous voulez la longueur réelle de la string, essayez utf16Count.

    println ("(str1.utf16Count), (str2.utf16Count)")

    MODIFIER:

    Notez que le calcul de la longueur d'une string nécessite l'itération sur tous les caractères et est donc une opération O (N). La raison en est que différents caractères nécessitent des quantités variables de memory à stocker. Alors que les caractères les plus couramment utilisés correspondent à 16 ou même 8 bits, d'autres comme emoji ont besoin de 32 bits2, et le stockage requirejs pour un cluster graphème est théoriquement illimité car un caractère de base peut avoir des marques de combinaison illimitées.

    Donc, pour calculer la longueur réelle:

     var length = 0 for char in str1 { length = length + 1 } println(length)