Découvrez si le caractère dans la chaîne est un emoji?


90

J'ai besoin de savoir si un caractère dans une chaîne est un emoji.

Par exemple, j'ai ce caractère:

let string = "😀"
let character = Array(string)[0]

J'ai besoin de savoir si ce personnage est un emoji.


Je suis curieux: pourquoi avez-vous besoin de ces informations?
Martin R

@EricD .: Il existe de nombreux caractères Unicode qui prennent plus d'un point de code UTF-8 (par exemple "€" = E2 82 AC) ou plus d'un point de code UTF-16 (par exemple "𝄞" = D834 DD1E).
Martin R

J'espère que vous aurez une idée de cette version obj-c du code stackoverflow.com/questions/19886642/...
Ashish Kakkad

Les chaînes ont leur indexation qui est une manière préférée de les utiliser. Pour obtenir un caractère particulier (ou un groupe de graphèmes plutôt), vous pouvez: let character = string[string.index(after: string.startIndex)]ou let secondCharacter = string[string.index(string.startIndex, offsetBy: 1)]
Paul B

Réponses:


229

Ce sur quoi je suis tombé, c'est la différence entre les caractères, les scalaires Unicode et les glyphes.

Par exemple, le glyphe 👨‍👨‍👧‍👧 se compose de 7 scalaires unicode:

Autre exemple, le glyphe 👌🏿 se compose de 2 scalaires unicode:

  • L'emoji régulier: 👌
  • Un modificateur de teint: 🏿

Le dernier, le glyphe 1️⃣ contient trois caractères unicode:

Ainsi, lors du rendu des caractères, les glyphes résultants comptent vraiment.

Swift 5.0 et supérieur rend ce processus beaucoup plus facile et élimine certaines hypothèses que nous devions faire. Unicode.ScalarLe nouveau Propertytype d 'aide est de déterminer à quoi nous avons affaire. Cependant, ces propriétés n'ont de sens que lors de la vérification des autres scalaires dans le glyphe. C'est pourquoi nous ajouterons des méthodes pratiques à la classe Character pour nous aider.

Pour plus de détails, j'ai écrit un article expliquant comment cela fonctionne .

Pour Swift 5.0, cela vous laisse le résultat suivant:

extension Character {
    /// A simple emoji is one scalar and presented to the user as an Emoji
    var isSimpleEmoji: Bool {
        guard let firstScalar = unicodeScalars.first else { return false }
        return firstScalar.properties.isEmoji && firstScalar.value > 0x238C
    }

    /// Checks if the scalars will be merged into an emoji
    var isCombinedIntoEmoji: Bool { unicodeScalars.count > 1 && unicodeScalars.first?.properties.isEmoji ?? false }

    var isEmoji: Bool { isSimpleEmoji || isCombinedIntoEmoji }
}

extension String {
    var isSingleEmoji: Bool { count == 1 && containsEmoji }

    var containsEmoji: Bool { contains { $0.isEmoji } }

    var containsOnlyEmoji: Bool { !isEmpty && !contains { !$0.isEmoji } }

    var emojiString: String { emojis.map { String($0) }.reduce("", +) }

    var emojis: [Character] { filter { $0.isEmoji } }

    var emojiScalars: [UnicodeScalar] { filter { $0.isEmoji }.flatMap { $0.unicodeScalars } }
}

Ce qui vous donnera les résultats suivants:

"A̛͚̖".containsEmoji // false
"3".containsEmoji // false
"A̛͚̖▶️".unicodeScalars // [65, 795, 858, 790, 9654, 65039]
"A̛͚̖▶️".emojiScalars // [9654, 65039]
"3️⃣".isSingleEmoji // true
"3️⃣".emojiScalars // [51, 65039, 8419]
"👌🏿".isSingleEmoji // true
"🙎🏼‍♂️".isSingleEmoji // true
"🇹🇩".isSingleEmoji // true
"⏰".isSingleEmoji // true
"🌶".isSingleEmoji // true
"👨‍👩‍👧‍👧".isSingleEmoji // true
"🏴󠁧󠁢󠁳󠁣󠁴󠁿".isSingleEmoji // true
"🏴󠁧󠁢󠁥󠁮󠁧󠁿".containsOnlyEmoji // true
"👨‍👩‍👧‍👧".containsOnlyEmoji // true
"Hello 👨‍👩‍👧‍👧".containsOnlyEmoji // false
"Hello 👨‍👩‍👧‍👧".containsEmoji // true
"👫 Héllo 👨‍👩‍👧‍👧".emojiString // "👫👨‍👩‍👧‍👧"
"👨‍👩‍👧‍👧".count // 1

"👫 Héllœ 👨‍👩‍👧‍👧".emojiScalars // [128107, 128104, 8205, 128105, 8205, 128103, 8205, 128103]
"👫 Héllœ 👨‍👩‍👧‍👧".emojis // ["👫", "👨‍👩‍👧‍👧"]
"👫 Héllœ 👨‍👩‍👧‍👧".emojis.count // 2

"👫👨‍👩‍👧‍👧👨‍👨‍👦".isSingleEmoji // false
"👫👨‍👩‍👧‍👧👨‍👨‍👦".containsOnlyEmoji // true

Pour les anciennes versions de Swift, consultez cet essentiel contenant mon ancien code.


6
C'est de loin la meilleure et la plus correcte réponse ici. Merci! Une petite note, vos exemples ne correspondent pas au code (vous avez renommé containsOnlyEmoki en containsEmoji dans l'extrait de code - je suppose que parce que c'est plus correct, dans mes tests, il a renvoyé true pour les chaînes avec des caractères mixtes).
Tim Bull le

3
Mon mauvais, j'ai changé de code, je suppose que j'ai raté. J'ai mis à jour l'exemple
Kevin R

2
@Andrew: Bien sûr, j'ai ajouté une autre méthode à l'exemple pour le démontrer :).
Kevin R

2
@Andrew c'est là que ça devient vraiment compliqué. J'ai ajouté un exemple comment faire cela. Le problème est que j'ai supposé savoir comment CoreText rendra les glyphes en vérifiant simplement les caractères. Si quelqu'un a des suggestions pour une méthode plus propre, veuillez me le faire savoir.
Kevin R

3
@Andrew Merci d'avoir signalé cela, j'ai changé la façon dont les containsOnlyEmojicontrôles. J'ai également mis à jour l'exemple vers Swift 3.0.
Kevin R

48

Le moyen le plus simple, le plus propre et le plus rapide d'y parvenir est de simplement vérifier les points de code Unicode pour chaque caractère de la chaîne par rapport aux plages d'emoji et de dingbats connues, comme ceci:

extension String {

    var containsEmoji: Bool {
        for scalar in unicodeScalars {
            switch scalar.value {
            case 0x1F600...0x1F64F, // Emoticons
                 0x1F300...0x1F5FF, // Misc Symbols and Pictographs
                 0x1F680...0x1F6FF, // Transport and Map
                 0x2600...0x26FF,   // Misc symbols
                 0x2700...0x27BF,   // Dingbats
                 0xFE00...0xFE0F,   // Variation Selectors
                 0x1F900...0x1F9FF, // Supplemental Symbols and Pictographs
                 0x1F1E6...0x1F1FF: // Flags
                return true
            default:
                continue
            }
        }
        return false
    }

}

9
Un exemple de code comme celui-ci est bien meilleur que de suggérer d'inclure une dépendance de bibliothèque tierce. La réponse de Shardul est un conseil imprudent à suivre: écrivez toujours votre propre code.
thefaj

C'est génial, merci d'avoir commenté ce à quoi les affaires se rapportent
Shawn Throop

1
Comme beaucoup votre code, je l'ai implémenté dans une réponse ici . Une chose que j'ai remarquée, c'est qu'il manque des emoji, peut-être parce qu'ils ne font pas partie des catégories que vous avez énumérées, par exemple celle-ci: Robot Face emoji 🤖
Cue

1
@Tel Je suppose que ce serait la plage 0x1F900...0x1F9FF(par Wikipedia). Pas sûr que toute la gamme doive être considérée comme des emoji.
Frizlab

8
extension String {
    func containsEmoji() -> Bool {
        for scalar in unicodeScalars {
            switch scalar.value {
            case 0x3030, 0x00AE, 0x00A9,// Special Characters
            0x1D000...0x1F77F,          // Emoticons
            0x2100...0x27BF,            // Misc symbols and Dingbats
            0xFE00...0xFE0F,            // Variation Selectors
            0x1F900...0x1F9FF:          // Supplemental Symbols and Pictographs
                return true
            default:
                continue
            }
        }
        return false
    }
}

C'est ma solution, avec des plages mises à jour.


8

Swift 5.0

… Introduit une nouvelle façon de vérifier exactement cela!

Vous devez casser votre Stringdans son Scalars. Chacun Scalara une Propertyvaleur qui soutient la isEmojivaleur!

En fait, vous pouvez même vérifier si le Scalar est un modificateur Emoji ou plus. Consultez la documentation d'Apple: https://developer.apple.com/documentation/swift/unicode/scalar/properties

Vous voudrez peut-être envisager de vérifier isEmojiPresentationplutôt que isEmoji, car Apple déclare ce qui suit pour isEmoji:

Cette propriété est vraie pour les scalaires qui sont rendus sous forme d'emoji par défaut et également pour les scalaires qui ont un rendu d'emoji non par défaut lorsqu'ils sont suivis par U + FE0F VARIATION SELECTOR-16. Cela inclut certains scalaires qui ne sont généralement pas considérés comme des emoji.


De cette façon, les Emoji sont divisés en tous les modificateurs, mais c'est beaucoup plus simple à gérer. Et comme Swift compte désormais les Emoji avec des modificateurs (par exemple: 👨‍👩‍👧‍👦, 👨🏻‍💻, 🏴) comme 1, vous pouvez faire toutes sortes de choses.

var string = "🤓 test"

for scalar in string.unicodeScalars {
    let isEmoji = scalar.properties.isEmoji

    print("\(scalar.description) \(isEmoji)"))
}

// 🤓 true
//   false
// t false
// e false
// s false
// t false

NSHipster souligne un moyen intéressant d'obtenir tous les Emoji:

import Foundation

var emoji = CharacterSet()

for codePoint in 0x0000...0x1F0000 {
    guard let scalarValue = Unicode.Scalar(codePoint) else {
        continue
    }

    // Implemented in Swift 5 (SE-0221)
    // https://github.com/apple/swift-evolution/blob/master/proposals/0221-character-properties.md
    if scalarValue.properties.isEmoji {
        emoji.insert(scalarValue)
    }
}

1
Excellente réponse, merci. Il est à noter que votre sdk minimum doit être de 10,2 pour utiliser cette partie de Swift 5. Aussi pour vérifier si une chaîne était uniquement composée d'émojis, j'ai dû vérifier si elle avait l'une de ces propriétés:scalar.properties.isEmoji scalar.properties.isEmojiPresentation scalar.properties.isEmojiModifier scalar.properties.isEmojiModifierBase scalar.properties.isJoinControl scalar.properties.isVariationSelector
A Springham

6
Attention, les entiers 0-9 sont considérés comme des emojis. Donc "6".unicodeScalars.first!.properties.isEmojiévaluera commetrue
Miniroo

6

Avec Swift 5, vous pouvez désormais inspecter les propriétés unicode de chaque caractère de votre chaîne. Cela nous donne la isEmojivariable pratique sur chaque lettre. Le problème est isEmojiqu'il renvoie vrai pour tout caractère qui peut être converti en un emoji de 2 octets, tel que 0-9.

Nous pouvons regarder la variable isEmojiet également vérifier la présence d'un modificateur d'emoji pour déterminer si les caractères ambigus s'afficheront sous forme d'emoji.

Cette solution devrait être bien plus à l'épreuve du temps que les solutions regex proposées ici.

extension String {
    func containsOnlyEmojis() -> Bool {
        if count == 0 {
            return false
        }
        for character in self {
            if !character.isEmoji {
                return false
            }
        }
        return true
    }
    
    func containsEmoji() -> Bool {
        for character in self {
            if character.isEmoji {
                return true
            }
        }
        return false
    }
}

extension Character {
    // An emoji can either be a 2 byte unicode character or a normal UTF8 character with an emoji modifier
    // appended as is the case with 3️⃣. 0x238C is the first instance of UTF16 emoji that requires no modifier.
    // `isEmoji` will evaluate to true for any character that can be turned into an emoji by adding a modifier
    // such as the digit "3". To avoid this we confirm that any character below 0x238C has an emoji modifier attached
    var isEmoji: Bool {
        guard let scalar = unicodeScalars.first else { return false }
        return scalar.properties.isEmoji && (scalar.value > 0x238C || unicodeScalars.count > 1)
    }
}

Nous donnant

"hey".containsEmoji() //false

"Hello World 😎".containsEmoji() //true
"Hello World 😎".containsOnlyEmojis() //false

"3".containsEmoji() //false
"3️⃣".containsEmoji() //true

1
Et ce qui est plus est Character("3️⃣").isEmoji // truewhileCharacter("3").isEmoji // false
Paul B

4

Remarque sur Swift 3:

Il semble que la cnui_containsEmojiCharactersméthode ait été supprimée ou déplacée vers une autre bibliothèque dynamique. _containsEmojidevrait toujours fonctionner.

let str: NSString = "hello😊"

@objc protocol NSStringPrivate {
    func _containsEmoji() -> ObjCBool
}

let strPrivate = unsafeBitCast(str, to: NSStringPrivate.self)
strPrivate._containsEmoji() // true
str.value(forKey: "_containsEmoji") // 1


let swiftStr = "hello😊"
(swiftStr as AnyObject).value(forKey: "_containsEmoji") // 1

Swift 2.x:

J'ai récemment découvert une API privée sur NSStringlaquelle expose des fonctionnalités pour détecter si une chaîne contient un caractère Emoji:

let str: NSString = "hello😊"

Avec un protocole objc et unsafeBitCast:

@objc protocol NSStringPrivate {
    func cnui_containsEmojiCharacters() -> ObjCBool
    func _containsEmoji() -> ObjCBool
}

let strPrivate = unsafeBitCast(str, NSStringPrivate.self)
strPrivate.cnui_containsEmojiCharacters() // true
strPrivate._containsEmoji() // true

Avec valueForKey:

str.valueForKey("cnui_containsEmojiCharacters") // 1
str.valueForKey("_containsEmoji") // 1

Avec une chaîne Swift pure, vous devez convertir la chaîne comme AnyObjectavant d'utiliser valueForKey:

let str = "hello😊"

(str as AnyObject).valueForKey("cnui_containsEmojiCharacters") // 1
(str as AnyObject).valueForKey("_containsEmoji") // 1

Méthodes trouvées dans le fichier d'en-tête NSString .


C'est ce que je recherche, merci JAL

Cela sera-t-il rejeté par Apple?
Andrey Chernukha

@AndreyChernukha Il y a toujours un risque, mais je n'ai pas encore connu de rejet.
JAL

N'utilisez jamais d'API privées. Au mieux, la blessure ne viendra que demain. Ou le mois prochain.
xaphod

3

Vous pouvez utiliser cet exemple de code ou ce pod .

Pour l'utiliser dans Swift, importez la catégorie dans le YourProject_Bridging_Header

#import "NSString+EMOEmoji.h"

Ensuite, vous pouvez vérifier la plage de chaque emoji de votre chaîne:

let example: NSString = "string👨‍👨‍👧‍👧with😍emojis✊🏿" //string with emojis

let containsEmoji: Bool = example.emo_containsEmoji()

    print(containsEmoji)

// Output: ["true"]

J'ai créé un petit exemple de projet avec le code ci-dessus.


3

Preuve future: vérifiez manuellement les pixels du personnage; les autres solutions se briseront (et se seront cassées) à mesure que de nouveaux emojis seront ajoutés.

Remarque: c'est Objective-C (peut être converti en Swift)

Au fil des ans, ces solutions de détection des emojis continuent de se produire à mesure qu'Apple ajoute de nouveaux emojis avec de nouvelles méthodes (comme des emojis toniques de peau construits en pré-maudissant un personnage avec un caractère supplémentaire), etc.

Je suis finalement tombé en panne et j'ai juste écrit la méthode suivante qui fonctionne pour tous les emojis actuels et devrait fonctionner pour tous les futurs emojis.

La solution crée un UILabel avec le caractère et un fond noir. CG prend ensuite un instantané de l'étiquette et je scanne tous les pixels de l'instantané pour tout pixel non noir uni. La raison pour laquelle j'ajoute le fond noir est d'éviter les problèmes de fausse coloration dus au rendu de sous-pixels

La solution fonctionne TRÈS vite sur mon appareil, je peux vérifier des centaines de caractères par seconde, mais il convient de noter qu'il s'agit d'une solution CoreGraphics et ne doit pas être utilisée de manière intensive comme vous le feriez avec une méthode de texte ordinaire. Le traitement graphique est lourd en données, donc la vérification de milliers de caractères à la fois peut entraîner un retard notable.

-(BOOL)isEmoji:(NSString *)character {
    
    UILabel *characterRender = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
    characterRender.text = character;
    characterRender.font = [UIFont fontWithName:@"AppleColorEmoji" size:12.0f];//Note: Size 12 font is likely not crucial for this and the detector will probably still work at an even smaller font size, so if you needed to speed this checker up for serious performance you may test lowering this to a font size like 6.0
    characterRender.backgroundColor = [UIColor blackColor];//needed to remove subpixel rendering colors
    [characterRender sizeToFit];
    
    CGRect rect = [characterRender bounds];
    UIGraphicsBeginImageContextWithOptions(rect.size,YES,0.0f);
    CGContextRef contextSnap = UIGraphicsGetCurrentContext();
    [characterRender.layer renderInContext:contextSnap];
    UIImage *capturedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    CGImageRef imageRef = [capturedImage CGImage];
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
    NSUInteger bytesPerPixel = 4;//Note: Alpha Channel not really needed, if you need to speed this up for serious performance you can refactor this pixel scanner to just RGB
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;
    CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                                                 bitsPerComponent, bytesPerRow, colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);
    
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
    CGContextRelease(context);
    
    BOOL colorPixelFound = NO;
    
    int x = 0;
    int y = 0;
    while (y < height && !colorPixelFound) {
        while (x < width && !colorPixelFound) {
            
            NSUInteger byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
            
            CGFloat red = (CGFloat)rawData[byteIndex];
            CGFloat green = (CGFloat)rawData[byteIndex+1];
            CGFloat blue = (CGFloat)rawData[byteIndex+2];
            
            CGFloat h, s, b, a;
            UIColor *c = [UIColor colorWithRed:red green:green blue:blue alpha:1.0f];
            [c getHue:&h saturation:&s brightness:&b alpha:&a];//Note: I wrote this method years ago, can't remember why I check HSB instead of just checking r,g,b==0; Upon further review this step might not be needed, but I haven't tested to confirm yet. 
            
            b /= 255.0f;
            
            if (b > 0) {
                colorPixelFound = YES;
            }
            
            x++;
        }
        x=0;
        y++;
    }
    
    return colorPixelFound;
    
}

4
J'aime ta réflexion! ;) - Hors de la boîte!
Ramon

Pourquoi tu nous fais ça? #apple #unicodestandard 😱🤔🤪🙈😈🤕💩
d4Rk

Je n'ai pas regardé cela depuis un moment mais je me demande si je dois convertir en UIColor puis en hsb; il semble que je puisse simplement vérifier que r, g, b tous == 0? Si quelqu'un essaie de me le faire savoir
Albert Renshaw

J'aime cette solution, mais ne va-t-elle pas rompre avec un personnage comme ℹ?
Juan Carlos Ospina Gonzalez

1
@JuanCarlosOspinaGonzalez Non, en emoji qui se traduit par une boîte bleue avec un i blanc. Cela soulève cependant un bon point que l'UILabel devrait forcer la police à être AppleColorEmoji, ajoutant que maintenant comme une sécurité intégrée , bien que je pense qu'Apple le mettra par défaut pour ceux-là de toute façon
Albert Renshaw

2

Pour Swift 3.0.2, la réponse suivante est la plus simple:

class func stringContainsEmoji (string : NSString) -> Bool
{
    var returnValue: Bool = false

    string.enumerateSubstrings(in: NSMakeRange(0, (string as NSString).length), options: NSString.EnumerationOptions.byComposedCharacterSequences) { (substring, substringRange, enclosingRange, stop) -> () in

        let objCString:NSString = NSString(string:substring!)
        let hs: unichar = objCString.character(at: 0)
        if 0xd800 <= hs && hs <= 0xdbff
        {
            if objCString.length > 1
            {
                let ls: unichar = objCString.character(at: 1)
                let step1: Int = Int((hs - 0xd800) * 0x400)
                let step2: Int = Int(ls - 0xdc00)
                let uc: Int = Int(step1 + step2 + 0x10000)

                if 0x1d000 <= uc && uc <= 0x1f77f
                {
                    returnValue = true
                }
            }
        }
        else if objCString.length > 1
        {
            let ls: unichar = objCString.character(at: 1)
            if ls == 0x20e3
            {
                returnValue = true
            }
        }
        else
        {
            if 0x2100 <= hs && hs <= 0x27ff
            {
                returnValue = true
            }
            else if 0x2b05 <= hs && hs <= 0x2b07
            {
                returnValue = true
            }
            else if 0x2934 <= hs && hs <= 0x2935
            {
                returnValue = true
            }
            else if 0x3297 <= hs && hs <= 0x3299
            {
                returnValue = true
            }
            else if hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50
            {
                returnValue = true
            }
        }
    }

    return returnValue;
}

2

La réponse absolument similaire à celles qui ont écrit avant moi, mais avec un ensemble mis à jour de scalaires emoji.

extension String {
    func isContainEmoji() -> Bool {
        let isContain = unicodeScalars.first(where: { $0.isEmoji }) != nil
        return isContain
    }
}


extension UnicodeScalar {

    var isEmoji: Bool {
        switch value {
        case 0x1F600...0x1F64F,
             0x1F300...0x1F5FF,
             0x1F680...0x1F6FF,
             0x1F1E6...0x1F1FF,
             0x2600...0x26FF,
             0x2700...0x27BF,
             0xFE00...0xFE0F,
             0x1F900...0x1F9FF,
             65024...65039,
             8400...8447,
             9100...9300,
             127000...127600:
            return true
        default:
            return false
        }
    }

}


0

Il existe une solution intéressante pour la tâche mentionnée. Mais vérifier les propriétés Unicode.Scalar.Properties des scalaires Unicode est bon pour un seul caractère. Et pas assez flexible pour les cordes.

Nous pouvons utiliser des expressions régulières à la place - approche plus universelle. Vous trouverez ci-dessous une description détaillée de son fonctionnement. Et voici la solution.

La solution

Dans Swift, vous pouvez vérifier si une chaîne est un seul caractère Emoji, en utilisant une extension avec une telle propriété calculée:

extension String {

    var isSingleEmoji : Bool {
        if self.count == 1 {
            let emodjiGlyphPattern = "\\p{RI}{2}|(\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})|[\\p{Emoji}&&\\p{Other_symbol}])(\\x{200D}(\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})|[\\p{Emoji}&&\\p{Other_symbol}]))*"

            let fullRange = NSRange(location: 0, length: self.utf16.count)
            if let regex = try? NSRegularExpression(pattern: emodjiGlyphPattern, options: .caseInsensitive) {
                let regMatches = regex.matches(in: self, options: NSRegularExpression.MatchingOptions(), range: fullRange)
                if regMatches.count > 0 {
                    // if any range found — it means, that that single character is emoji
                    return true
                }
            }
        }
        return false
    }

}

Comment ça marche (en détails)

Un seul Emoji (un glyphe) peut être reproduit par un certain nombre de symboles, de séquences et de combinaisons différents. La spécification Unicode définit plusieurs représentations de caractères Emoji possibles.

Emoji à un seul caractère

Un personnage Emoji reproduit par un seul scalaire Unicode.

Unicode définit le caractère Emoji comme:

emoji_character := \p{Emoji}

Mais cela ne signifie pas nécessairement qu'un tel personnage sera dessiné comme un Emoji. Un symbole numérique ordinaire «1» a la propriété Emoji étant vrai, bien qu'il puisse toujours être dessiné sous forme de texte. Et il y a une liste de ces symboles: #, ©, 4, etc.

Il faut penser que nous pouvons utiliser une propriété supplémentaire pour vérifier: "Emoji_Presentation". Mais ça ne marche pas comme ça. Il existe un Emoji comme 🏟 ou 🛍, qui ont la propriété Emoji_Presentation = false.

Pour nous assurer que le personnage est dessiné comme Emoji par défaut, nous devons vérifier sa catégorie: il doit s'agir de «Other_symbol».

Donc, en fait, l'expression régulière pour Emoji à un seul caractère doit être définie comme:

emoji_character := \p{Emoji}&&\p{Other_symbol}

Séquence de présentation Emoji

Un caractère, qui peut normalement être dessiné sous forme de texte ou d'Emoji. Son apparence dépend d'un symbole spécial suivant, un sélecteur de présentation, qui indique son type de présentation. \ x {FE0E} définit la représentation textuelle. \ x {FE0F} définit la représentation emoji.

La liste de ces symboles peut être trouvée [ici] (
 https://unicode.org/Public/emoji/12.1/emoji-variation-sequences.txt ).

Unicode définit la séquence de présentation comme ceci:

emoji_presentation_sequence := emoji_character emoji_presentation_selector

Séquence d'expression régulière pour cela:

emoji_presentation_sequence := \p{Emoji} \x{FE0F}

Séquence de touches Emoji

La séquence ressemble beaucoup à la séquence de présentation, mais elle a un scalaire supplémentaire à la fin: \ x {20E3}. La portée des scalaires de base possibles utilisés pour cela est plutôt étroite: 0-9 # * - et c'est tout. Exemples: 1️⃣, 8️⃣, * ️⃣.

Unicode définit la séquence de touches comme ceci:

emoji_keycap_sequence := [0-9#*] \x{FE0F 20E3}

Expression régulière pour cela:

emoji_keycap_sequence := \p{Emoji} \x{FE0F} \x{FE0F}

Séquence de modificateurs Emoji

Certains Emojis peuvent avoir une apparence modifiée comme un teint. Par exemple, Emoji 🧑 peut être différent: 🧑🧑🏻🧑🏼🧑🏽🧑🏾🧑🏿. Pour définir un Emoji, qui est appelé "Emoji_Modifier_Base" dans ce cas, on peut utiliser un "Emoji_Modifier" ultérieur.

En général, une telle séquence ressemble à ceci:

emoji_modifier_sequence := emoji_modifier_base emoji_modifier

Pour le détecter, nous pouvons rechercher une séquence d'expressions régulières:

emoji_modifier_sequence := \p{Emoji} \p{EMod}

Séquence de drapeau Emoji

Les drapeaux sont des Emojis avec leur structure particulière. Chaque drapeau est représenté par deux symboles «Regional_Indicator».

Unicode les définit comme:

emoji_flag_sequence := regional_indicator regional_indicator

Par exemple, le drapeau de l'Ukraine 🇺🇦 est en fait représenté par deux scalaires: \ u {0001F1FA \ u {0001F1E6}

Expression régulière pour cela:

emoji_flag_sequence := \p{RI}{2}

Séquence de balises Emoji (ETS)

Une séquence qui utilise ce que l'on appelle tag_base, qui est suivie d'une spécification de balise personnalisée composée d'une plage de symboles \ x {E0020} - \ x {E007E} et conclue par tag_end mark \ x {E007F}.

Unicode le définit comme ceci:

emoji_tag_sequence := tag_base tag_spec tag_end
tag_base           := emoji_character
                    | emoji_modifier_sequence
                    | emoji_presentation_sequence
tag_spec           := [\x{E0020}-\x{E007E}]+
tag_end            := \x{E007F}

Ce qui est étrange, c'est qu'Unicode permet aux balises d'être basées sur emoji_modifier_sequence ou emoji_presentation_sequence dans ED-14a . Mais en même temps, dans les expressions régulières fournies dans la même documentation, ils semblent vérifier la séquence basée sur un seul caractère Emoji.

Dans la liste Unicode 12.1 Emoji, seuls trois Emojis sont définis. Tous sont des drapeaux des pays du Royaume-Uni: l'Angleterre 🏴󠁧󠁢󠁥󠁮󠁧󠁿, l'Écosse 🏴󠁧󠁢󠁳󠁣󠁴󠁿 et le Pays de Galles 🏴󠁧󠁢󠁷󠁬󠁳󠁿. Et tous sont basés sur un seul personnage Emoji. Donc, nous ferions mieux de vérifier uniquement une telle séquence.

Expression régulière:

\p{Emoji} [\x{E0020}-\x{E007E}]+ \x{E007F}

Séquence de jointure Emoji de largeur zéro (séquence ZWJ)

Un jointeur de largeur nulle est un scalaire \ x {200D}. Avec son aide, plusieurs personnages, qui sont déjà des Emojis par eux-mêmes, peuvent être combinés en de nouveaux.

Par exemple, une «famille avec père, fils et fille» Emoji 👨‍👧‍👦 est reproduite par une combinaison de père 👨, fille 👧 et fils 👦 Emojis collés avec les symboles ZWJ.

Il est permis de coller ensemble des éléments, qui sont des caractères Emoji uniques, des séquences de présentation et de modification.

L'expression régulière pour une telle séquence ressemble en général à ceci:

emoji_zwj_sequence := emoji_zwj_element (\x{200d} emoji_zwj_element )+

Expression régulière pour tous

Toutes les représentations Emoji mentionnées ci-dessus peuvent être décrites par une seule expression régulière:

\p{RI}{2}
| ( \p{Emoji} 
    ( \p{EMod} 
    | \x{FE0F}\x{20E3}? 
    | [\x{E0020}-\x{E007E}]+\x{E007F} 
    ) 
  | 
[\p{Emoji}&&\p{Other_symbol}] 
  )
  ( \x{200D}
    ( \p{Emoji} 
      ( \p{EMod} 
      | \x{FE0F}\x{20E3}? 
      | [\x{E0020}-\x{E007E}]+\x{E007F} 
      ) 
    | [\p{Emoji}&&\p{Other_symbol}] 
    ) 
  )*

-1

J'ai eu le même problème et j'ai fini par faire des extensions Stringet Character.

Le code est trop long à publier car il répertorie en fait tous les emojis (de la liste officielle Unicode v5.0) dans un, CharacterSetvous pouvez le trouver ici:

https://github.com/piterwilson/StringEmoji

Constantes

laissez emojiCharacterSet: CharacterSet

Jeu de caractères contenant tous les emoji connus (comme décrit dans la liste officielle Unicode 5.0 http://unicode.org/emoji/charts-5.0/emoji-list.html )

Chaîne

var isEmoji: Bool {get}

Si l' Stringinstance représente ou non un seul caractère Emoji connu

print("".isEmoji) // false
print("😁".isEmoji) // true
print("😁😜".isEmoji) // false (String is not a single Emoji)
var contientEmoji: Bool {get}

Si l' Stringinstance contient ou non un caractère Emoji connu

print("".containsEmoji) // false
print("😁".containsEmoji) // true
print("😁😜".containsEmoji) // true
var unicodeName: String {get}

Applique un kCFStringTransformToUnicodeName- CFStringTransformsur une copie de la chaîne

print("á".unicodeName) // \N{LATIN SMALL LETTER A WITH ACUTE}
print("😜".unicodeName) // "\N{FACE WITH STUCK-OUT TONGUE AND WINKING EYE}"
var niceUnicodeName: String {get}

Renvoie le résultat d'un kCFStringTransformToUnicodeName- CFStringTransformavec les \N{préfixes et }suffixes supprimés

print("á".unicodeName) // LATIN SMALL LETTER A WITH ACUTE
print("😜".unicodeName) // FACE WITH STUCK-OUT TONGUE AND WINKING EYE

Personnage

var isEmoji: Bool {get}

Si l' Characterinstance représente ou non un caractère Emoji connu

print("".isEmoji) // false
print("😁".isEmoji) // true
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.