Scopri se Character in String è emoji?


92

Devo scoprire se un carattere in una stringa è un'emoji.

Ad esempio, ho questo personaggio:

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

Devo scoprire se quel personaggio è un'emoji.


Sono curioso: perché hai bisogno di queste informazioni?
Martin R

@EricD .: Ci sono molti caratteri Unicode che accettano più di un punto di codice UTF-8 (ad esempio "€" = E2 82 AC) o più di un punto di codice UTF-16 (ad esempio "𝄞" = D834 DD1E).
Martin R

Spero che avuto un'idea da questa versione obj-c del Codice stackoverflow.com/questions/19886642/...
Ashish Kakkad

Le stringhe hanno la loro indicizzazione che è un modo preferito di usarle. Per ottenere un particolare personaggio (o piuttosto grapheme cluster) potresti: let character = string[string.index(after: string.startIndex)]o let secondCharacter = string[string.index(string.startIndex, offsetBy: 1)]
Paul B

Risposte:


239

Quello in cui mi sono imbattuto è la differenza tra caratteri, scalari unicode e glifi.

Ad esempio, il glifo 👨‍👨‍👧‍👧 è composto da 7 scalari unicode:

Un altro esempio, il glifo 👌🏿 è costituito da 2 scalari unicode:

  • L'emoji normale: 👌
  • Un modificatore del tono della pelle: 🏿

L'ultimo, il glifo 1️⃣ contiene tre caratteri Unicode:

Quindi, durante il rendering dei personaggi, i glifi risultanti contano davvero.

Swift 5.0 e versioni successive rendono questo processo molto più semplice e si liberano di alcune congetture che dovevamo fare. Unicode.ScalarIl nuovo Propertytipo di aiuto è determinare con cosa abbiamo a che fare. Tuttavia, queste proprietà hanno senso solo quando si controllano gli altri scalari all'interno del glifo. Questo è il motivo per cui aggiungeremo alcuni metodi convenienti alla classe Character per aiutarci.

Per maggiori dettagli, ho scritto un articolo che spiega come funziona .

Per Swift 5.0, ti lascia con il seguente risultato:

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

Che ti darà i seguenti risultati:

"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

Per le versioni precedenti di Swift, controlla questa sintesi contenente il mio vecchio codice.


6
Questa è di gran lunga la risposta migliore e più corretta qui. Grazie! Una piccola nota, i tuoi esempi non corrispondono al codice (hai rinominato containsOnlyEmoki in containsEmoji nello snippet - presumo perché è più corretto, nei miei test è tornato vero per le stringhe con caratteri misti).
Tim Bull

3
Colpa mia, ho cambiato del codice, immagino di aver sbagliato. Ho aggiornato l'esempio
Kevin R

2
@ Andrew: Certo, ho aggiunto un altro metodo all'esempio per dimostrarlo :).
Kevin R

2
@Andrew questo è dove diventa davvero disordinato. Ho aggiunto un esempio su come farlo. Il problema è che presumo di sapere come CoreText renderà i glifi semplicemente controllando i caratteri. Se qualcuno ha suggerimenti per un metodo più pulito, fatemelo sapere.
Kevin R

3
@ Andrew Grazie per averlo sottolineato, ho cambiato il modo in cui containsOnlyEmojicontrolla. Ho anche aggiornato l'esempio a Swift 3.0.
Kevin R

51

Il modo più semplice, pulito e rapido per farlo è semplicemente controllare i punti di codice Unicode per ogni carattere nella stringa rispetto agli intervalli noti di emoji e dingbats, in questo modo:

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 esempio di codice come questo è molto meglio che suggerire di includere una dipendenza da libreria di terze parti. La risposta di Shardul è un consiglio poco saggio da seguire: scrivi sempre il tuo codice.
thefaj

È fantastico, grazie per aver commentato a cosa si riferiscono i casi
Shawn Throop,

2
Come tanto il tuo codice, l'ho implementato in una risposta qui . Una cosa che ho notato è che mancano alcune emoji, forse perché non fanno parte delle categorie che hai elencato, ad esempio questa: Robot Face emoji 🤖
Cue

1
@Tel immagino che sarebbe l'intervallo 0x1F900...0x1F9FF(per Wikipedia). Non sono sicuro che tutta la gamma debba essere considerata emoji.
Frizlab

11

Swift 5.0

... ha introdotto un nuovo modo di controllare esattamente questo!

Bisogna rompere il vostro Stringnel suo Scalars. Ognuno Scalarha un Propertyvalore che supporta ilisEmoji valore!

In realtà puoi anche verificare se lo scalare è un modificatore Emoji o più. Controlla la documentazione di Apple: https://developer.apple.com/documentation/swift/unicode/scalar/properties

Potresti prendere in considerazione il controllo isEmojiPresentationinvece di isEmoji, perché Apple afferma quanto segue per isEmoji:

Questa proprietà è vera per gli scalari che vengono visualizzati come emoji per impostazione predefinita e anche per gli scalari che hanno un rendering emoji non predefinito quando seguito da U + FE0F VARIATION SELECTOR-16. Ciò include alcuni scalari che non sono generalmente considerati emoji.


In questo modo in realtà divide le Emoji in tutti i modificatori, ma è molto più semplice da gestire. E poiché Swift ora conta gli Emoji con modificatori (ad esempio: 👨‍👩‍👧‍👦, 👨🏻‍💻, 🏴) come 1, puoi fare qualsiasi cosa.

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 indica un modo interessante per ottenere tutte le 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
Ottima risposta, grazie. Vale la pena ricordare che il tuo min sdk deve essere 10.2 per usare questa parte di Swift 5. Inoltre, per verificare se una stringa era composta solo da emoji, ho dovuto controllare se avesse una di queste proprietà:scalar.properties.isEmoji scalar.properties.isEmojiPresentation scalar.properties.isEmojiModifier scalar.properties.isEmojiModifierBase scalar.properties.isJoinControl scalar.properties.isVariationSelector
A Springham

6
Attenzione, i numeri interi 0-9 sono considerati emoji. Quindi "6".unicodeScalars.first!.properties.isEmojivaluterà cometrue
Miniroo

1
Ci sono altri caratteri simili #e *anche questo restituirà vero per il isEmojicontrollo. isEmojiPresentationsembra funzionare meglio, almeno restituisce falseper 0...9, #, *e qualsiasi altro simbolo ho potuto provare su una tastiera inglese-statunitense. Qualcuno ha più esperienza con esso e sa se può essere considerato attendibile per la convalida dell'input?
Jan

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

Questa è la mia soluzione, con intervalli aggiornati.


7

Con Swift 5 ora puoi ispezionare le proprietà Unicode di ogni carattere nella tua stringa. Questo ci dà la comoda isEmojivariabile su ogni lettera. Il problema èisEmoji che restituirà vero per qualsiasi carattere che può essere convertito in un'emoji a 2 byte, come 0-9.

Possiamo guardare la variabile isEmojie anche verificare la presenza di un modificatore di emoji per determinare se i caratteri ambigui verranno visualizzati come emoji.

Questa soluzione dovrebbe essere molto più a prova di futuro rispetto alle soluzioni regex offerte qui.

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

Dandoci

"hey".containsEmoji() //false

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

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

1
E per di più è Character("3️⃣").isEmoji // truementreCharacter("3").isEmoji // false
Paul B

4

Swift 3 Nota:

Sembra che il cnui_containsEmojiCharactersmetodo sia stato rimosso o spostato in una libreria dinamica diversa. _containsEmojidovrebbe comunque funzionare.

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:

Recentemente ho scoperto un'API privata su NSStringcui espone funzionalità per rilevare se una stringa contiene un carattere Emoji:

let str: NSString = "hello😊"

Con un protocollo objc e unsafeBitCast:

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

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

Con valueForKey:

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

Con una stringa Swift pura, devi lanciare la stringa come AnyObjectprima di utilizzare valueForKey:

let str = "hello😊"

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

Metodi trovati nel file di intestazione NSString .


Questo è quello che sto cercando, grazie JAL

Questo verrà rifiutato da Apple?
Andrey Chernukha

@AndreyChernukha C'è sempre un rischio, ma non ho ancora sperimentato alcun rifiuto.
JAL

Non usare mai API private. Nella migliore delle ipotesi, il dolore arriverà solo domani. O il mese prossimo.
xaphod

3

Puoi usare questo esempio di codice o questo pod .

Per usarlo in Swift, importa la categoria nel file YourProject_Bridging_Header

#import "NSString+EMOEmoji.h"

Quindi puoi controllare l'intervallo per ogni emoji nella tua stringa:

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

let containsEmoji: Bool = example.emo_containsEmoji()

    print(containsEmoji)

// Output: ["true"]

Ho creato un piccolo progetto di esempio con il codice sopra.


3

A prova di futuro: controlla manualmente i pixel del personaggio; le altre soluzioni si interromperanno (e si interromperanno) quando verranno aggiunti nuovi emoji.

Nota: questo è Objective-C (può essere convertito in Swift)

Nel corso degli anni queste soluzioni di rilevamento di emoji continuano a rompersi mentre Apple aggiunge nuovi emoji con nuovi metodi (come gli emoji dai toni della pelle costruiti pre-maledicendo un personaggio con un carattere aggiuntivo), ecc.

Alla fine mi sono rotto e ho appena scritto il seguente metodo che funziona per tutti gli emoji attuali e dovrebbe funzionare per tutti gli emoji futuri.

La soluzione crea un'etichetta UIL con il personaggio e uno sfondo nero. CG quindi scatta un'istantanea dell'etichetta e io scannerizzo tutti i pixel nell'istantanea per eventuali pixel non neri. Il motivo per cui aggiungo lo sfondo nero è per evitare problemi di falsi colori dovuti a rendering dei pixel secondari

La soluzione funziona MOLTO velocemente sul mio dispositivo, posso controllare centinaia di caratteri al secondo, ma va notato che questa è una soluzione CoreGraphics e non dovrebbe essere utilizzata pesantemente come si potrebbe fare con un normale metodo di testo. L'elaborazione grafica è pesante per i dati, quindi il controllo di migliaia di caratteri contemporaneamente potrebbe comportare un notevole ritardo.

-(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;
    
}

5
Mi piace il tuo pensiero! ;) - Fuori dalla scatola!
Ramon

Perché ci stai facendo questo? #apple #unicodestandard 😱🤔🤪🙈😈🤕💩
d4Rk

Non lo guardo da un po 'ma mi chiedo se devo convertire in UIColor e poi in hsb; sembra che posso solo controllare che r, g, b all == 0? Se qualcuno ci prova, fammi sapere
Albert Renshaw,

mi piace questa soluzione, ma non si romperà con un personaggio come ℹ?
Juan Carlos Ospina Gonzalez

1
@JuanCarlosOspinaGonzalez No, nell'emoji che viene visualizzato come una scatola blu con una i bianca. Tuttavia, fa emergere un buon punto sul fatto che UILabel dovrebbe forzare il carattere ad essere AppleColorEmoji, aggiungendo che ora è un fail safe, anche se penso che Apple lo farà comunque di default per quelli
Albert Renshaw

2

Per Swift 3.0.2, la seguente risposta è la più semplice:

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 risposta assolutamente simile a quelle che hanno scritto prima di me, ma con set aggiornato di scalari 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
        }
    }

}


1

C'è una bella soluzione per il compito menzionato. Ma il controllo di Unicode.Scalar.Properties degli scalari unicode è buono per un singolo carattere. E non abbastanza flessibile per gli archi.

Possiamo invece usare le espressioni regolari - un approccio più universale. C'è una descrizione dettagliata di come funziona di seguito. E qui va la soluzione.

La soluzione

In Swift puoi verificare se una stringa è un singolo carattere Emoji, utilizzando un'estensione con una tale proprietà calcolata:

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
    }

}

Come funziona (in dettaglio)

Un singolo Emoji (un glifo) può essere riprodotto da un numero di simboli, sequenze e loro combinazioni differenti. Specifica Unicode definisce diverse possibili rappresentazioni di caratteri Emoji.

Emoji a carattere singolo

Un carattere Emoji riprodotto da un singolo scalare Unicode.

Unicode definisce il carattere Emoji come:

emoji_character := \p{Emoji}

Ma non significa necessariamente che un personaggio del genere verrà disegnato come un'emoji. Un normale simbolo numerico "1" ha la proprietà Emoji che è vera, sebbene possa comunque essere disegnato come testo. E c'è un elenco di tali simboli: #, ©, 4, ecc.

Si dovrebbe pensare che possiamo usare proprietà aggiuntive per controllare: "Emoji_Presentation". Ma non funziona così. C'è un'emoji come 🏟 o 🛍, che ha la proprietà Emoji_Presentation = false.

Per essere sicuri che il personaggio sia disegnato come Emoji per impostazione predefinita, dovremmo controllare la sua categoria: dovrebbe essere "Other_symbol".

Quindi, in effetti, l'espressione regolare per Emoji a carattere singolo dovrebbe essere definita come:

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

Sequenza di presentazione Emoji

Un personaggio, che normalmente può essere disegnato come testo o come Emoji. Il suo aspetto dipende da uno speciale simbolo seguente, un selettore di presentazione, che indica il tipo di presentazione. \ x {FE0E} definisce la rappresentazione del testo. \ x {FE0F} definisce la rappresentazione delle emoji.

L'elenco di tali simboli può essere trovato [qui] (
 https://unicode.org/Public/emoji/12.1/emoji-variation-sequences.txt ).

Unicode definisce la sequenza di presentazione in questo modo:

emoji_presentation_sequence := emoji_character emoji_presentation_selector

Sequenza di espressioni regolari per questo:

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

Emoji Keycap Sequence

La sequenza sembra molto simile alla sequenza di presentazione, ma ha uno scalare aggiuntivo alla fine: \ x {20E3}. L'ambito dei possibili scalari di base utilizzati è piuttosto ristretto: 0-9 # * - e questo è tutto. Esempi: 1️⃣, 8️⃣, * ️⃣.

Unicode definisce la sequenza di tasti in questo modo:

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

Espressione regolare per questo:

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

Sequenza del modificatore di emoji

Alcuni Emoji possono avere un aspetto modificato come il tono della pelle. Ad esempio Emoji 🧑 può essere diverso: 🧑🧑🏻🧑🏼🧑🏽🧑🏾🧑🏿. Per definire un Emoji, che in questo caso si chiama “Emoji_Modifier_Base”, si può utilizzare un successivo “Emoji_Modifier”.

In generale tale sequenza è simile a questa:

emoji_modifier_sequence := emoji_modifier_base emoji_modifier

Per rilevarlo possiamo cercare una sequenza di espressioni regolari:

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

Sequenza di flag di emoji

Le bandiere sono emoji con la loro particolare struttura. Ciascun flag è rappresentato con due simboli "Regional_Indicator".

Unicode li definisce come:

emoji_flag_sequence := regional_indicator regional_indicator

Ad esempio la bandiera dell'Ucraina 🇺🇦 infatti è rappresentata con due scalari: \ u {0001F1FA \ u {0001F1E6}

Espressione regolare per questo:

emoji_flag_sequence := \p{RI}{2}

Sequenza di tag emoji (ETS)

Una sequenza che utilizza un cosiddetto tag_base, che è seguita da una specifica di tag personalizzata composta da un intervallo di simboli \ x {E0020} - \ x {E007E} e conclusa da tag_end mark \ x {E007F}.

Unicode lo definisce in questo modo:

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}

La cosa strana è che Unicode consente ai tag di essere basati su emoji_modifier_sequence o emoji_presentation_sequence in ED-14a . Ma allo stesso tempo nelle espressioni regolari fornite nella stessa documentazione sembrano controllare la sequenza basandosi solo su un singolo carattere Emoji.

Nell'elenco Emoji Unicode 12.1 ci sono solo tre di questi Emoji definiti. Sono tutte bandiere dei paesi del Regno Unito: Inghilterra 🏴󠁧󠁢󠁥󠁮󠁧󠁿, Scozia 🏴󠁧󠁢󠁳󠁣󠁴󠁿 e Galles 🏴󠁧󠁢󠁷󠁬󠁳󠁿. E tutti sono basati su un singolo personaggio Emoji. Quindi, faremmo meglio a controllare solo una sequenza del genere.

Espressione regolare:

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

Emoji Zero-Width Joiner Sequence (sequenza ZWJ)

Un joiner di larghezza zero è un \ x {200D} scalare. Con il suo aiuto diversi personaggi, che sono già Emoji da soli, possono essere combinati in nuovi.

Ad esempio, una "famiglia con padre, figlio e figlia" Emoji 👨‍👧‍👦 è riprodotta da una combinazione di emoji padre 👨, figlia 👧 e figlio 👦 incollati insieme ai simboli ZWJ.

È consentito unire elementi, che sono caratteri Emoji singoli, sequenze di presentazione e modificatori.

L'espressione regolare per tale sequenza in generale è simile a questa:

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

Espressione regolare per tutti loro

Tutte le rappresentazioni Emoji sopra menzionate possono essere descritte da una singola espressione regolare:

\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

ho avuto lo stesso problema e ho finito per creare estensioni Stringe Character.

Il codice è troppo lungo per essere pubblicato poiché in realtà elenca tutti gli emoji (dall'elenco ufficiale Unicode v5.0) in un CharacterSetche puoi trovare qui:

https://github.com/piterwilson/StringEmoji

Costanti

lascia emojiCharacterSet: CharacterSet

Set di caratteri contenente tutte le emoji conosciute (come descritto in Unicode List 5.0 ufficiale http://unicode.org/emoji/charts-5.0/emoji-list.html )

Corda

var isEmoji: Bool {get}

Indica se l' Stringistanza rappresenta o meno un singolo carattere Emoji noto

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

Indica se l' Stringistanza contiene o meno un carattere Emoji noto

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

Applica a kCFStringTransformToUnicodeName- CFStringTransformsu una copia di String

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

Restituisce il risultato di un kCFStringTransformToUnicodeName- CFStringTransformcon \N{prefissi e }suffissi rimossi

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

Personaggio

var isEmoji: Bool {get}

Indica se l' Characteristanza rappresenta o meno un personaggio Emoji noto

print("".isEmoji) // false
print("😁".isEmoji) // true
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.