Finden Sie heraus, ob Character in String Emoji ist?

90

Ich muss herausfinden, ob ein Zeichen in einer Zeichenfolge ein Emoji ist.

Zum Beispiel habe ich diesen Charakter:

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

Ich muss herausfinden, ob dieser Charakter ein Emoji ist.

Andrew
quelle
Ich bin neugierig: Warum brauchen Sie diese Informationen?
Martin R
@EricD.: Es gibt viele Unicode-Zeichen, die mehr als einen UTF-8-Codepunkt (z. B. "€" = E2 82 AC) oder mehr als einen UTF-16-Codepunkt (z. B. "𝄞" = D834 DD1E) verwenden.
Martin R
Ich hoffe, Sie haben eine Idee von dieser obj-c-Version des Codes stackoverflow.com/questions/19886642/…
Ashish Kakkad
Strings haben ihre Indizierung, was eine bevorzugte Art der Verwendung ist. Um einen bestimmten Charakter (oder eher einen Graphemcluster) zu erhalten, könnten Sie: let character = string[string.index(after: string.startIndex)]oder let secondCharacter = string[string.index(string.startIndex, offsetBy: 1)]
Paul B

Antworten:

228

Worauf ich gestoßen bin, ist der Unterschied zwischen Zeichen, Unicode-Skalaren und Glyphen.

Zum Beispiel besteht die Glyphe 👨‍👨‍👧‍👧 aus 7 Unicode-Skalaren:

Ein weiteres Beispiel ist, dass die Glyphe 👌🏿 aus 2 Unicode-Skalaren besteht:

  • Das reguläre Emoji: 👌
  • Ein Hauttonmodifikator: 🏿

Zuletzt enthält die Glyphe 1️⃣ drei Unicode-Zeichen:

Beim Rendern der Zeichen sind die resultierenden Glyphen wirklich wichtig.

Swift 5.0 und höher vereinfacht diesen Prozess erheblich und beseitigt einige Vermutungen, die wir anstellen mussten. Unicode.Scalarist neuProperty Typ hilft zu bestimmen, womit wir es zu tun haben. Diese Eigenschaften sind jedoch nur sinnvoll, wenn die anderen Skalare in der Glyphe überprüft werden. Aus diesem Grund werden wir der Character-Klasse einige praktische Methoden hinzufügen, um uns zu helfen.

Für weitere Details habe ich einen Artikel geschrieben, in dem erklärt wird, wie dies funktioniert .

Für Swift 5.0 erhalten Sie folgendes Ergebnis:

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

Welches gibt Ihnen die folgenden Ergebnisse:

"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

Schauen Sie sich für ältere Swift-Versionen diese Liste an, die meinen alten Code enthält.

Kevin R.
quelle
6
Dies ist bei weitem die beste und richtigste Antwort hier. Vielen Dank! Eine kleine Anmerkung: Ihre Beispiele stimmen nicht mit dem Code überein (Sie haben im Snippet "includeOnlyEmoki" in "includesEmoji" umbenannt - ich nehme an, weil es korrekter ist, hat es bei meinen Tests für Zeichenfolgen mit gemischten Zeichen "true" zurückgegeben).
Tim Bull
3
Mein schlechtes, ich habe einen Code geändert, schätze ich habe es vermasselt. Ich habe das Beispiel aktualisiert
Kevin R
2
@ Andrew: Sicher, ich habe dem Beispiel eine andere Methode hinzugefügt, um dies zu demonstrieren :).
Kevin R
2
@ Andrew hier wird es wirklich chaotisch. Ich habe ein Beispiel hinzugefügt, wie das geht. Das Problem ist, dass ich davon ausgehen muss, dass CoreText die Glyphen durch einfaches Überprüfen der Zeichen rendert. Wenn jemand Vorschläge für eine sauberere Methode hat, lassen Sie es mich bitte wissen.
Kevin R
3
@ Andrew Danke, dass du darauf hingewiesen hast, ich habe die Art und Weise der containsOnlyEmojiÜberprüfung geändert . Ich habe das Beispiel auch auf Swift 3.0 aktualisiert.
Kevin R
47

Der einfachste, sauberste und schnellste Weg, dies zu erreichen, besteht darin, einfach die Unicode-Codepunkte für jedes Zeichen in der Zeichenfolge mit bekannten Emo- und Dingbats-Bereichen zu vergleichen, wie z.

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
    }

}
Arnold
quelle
9
Ein solches Codebeispiel ist weitaus besser als der Vorschlag, eine Bibliotheksabhängigkeit eines Drittanbieters einzuschließen. Sharduls Antwort ist ein unkluger Rat - schreiben Sie immer Ihren eigenen Code.
Thefaj
Das ist großartig, danke, dass Sie kommentiert haben, worauf sich die Fälle beziehen
Shawn Throop
1
Wie so viel Code, implementiert ich es in einer Antwort hier . Eine Sache, die mir aufgefallen ist, ist, dass einige Emoji fehlen, vielleicht weil sie nicht zu den von Ihnen aufgelisteten Kategorien gehören, zum Beispiel diese: Robot Face Emoji
Cue
1
@ Tel Ich denke, es wäre der Bereich 0x1F900...0x1F9FF(per Wikipedia). Nicht sicher, ob der gesamte Bereich als Emoji betrachtet werden sollte.
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
    }
}

Dies ist mein Fix mit aktualisierten Bereichen.

Sebastian Lopez
quelle
8

Swift 5.0

… Eine neue Methode eingeführt, um genau dies zu überprüfen!

Sie müssen Ihre Stringin seine brechen Scalars. Jeder Scalarhat einen PropertyWert, der das unterstütztisEmoji Wert !

Sie können sogar überprüfen, ob der Skalar ein Emoji-Modifikator oder mehr ist. Lesen Sie die Dokumentation von Apple: https://developer.apple.com/documentation/swift/unicode/scalar/properties

Sie können für die Prüfung zu prüfen, isEmojiPresentationstatt isEmoji, weil Apple die folgenden Zustände für isEmoji:

Diese Eigenschaft gilt für Skalare, die standardmäßig als Emoji gerendert werden, sowie für Skalare, die ein nicht standardmäßiges Emoji-Rendering aufweisen, gefolgt von U + FE0F VARIATION SELECTOR-16. Dies schließt einige Skalare ein, die normalerweise nicht als Emoji betrachtet werden.


Auf diese Weise werden Emojis tatsächlich in alle Modifikatoren aufgeteilt, aber es ist viel einfacher zu handhaben. Und da Swift jetzt Emojis mit Modifikatoren (z. B. 👨‍👩‍👧‍👦, 👨🏻‍💻, 🏴) als 1 zählt, können Sie alle möglichen Dinge tun.

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 weist auf einen interessanten Weg hin, um alle Emojis zu bekommen:

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)
    }
}
alexkaessner
quelle
1
Tolle Antwort, danke. Es ist erwähnenswert, dass Ihr min sdk 10.2 sein muss, um diesen Teil von Swift 5 zu verwenden. Um zu überprüfen, ob eine Zeichenfolge nur aus Emojis besteht, musste ich überprüfen, ob sie eine der folgenden Eigenschaften hat:scalar.properties.isEmoji scalar.properties.isEmojiPresentation scalar.properties.isEmojiModifier scalar.properties.isEmojiModifierBase scalar.properties.isJoinControl scalar.properties.isVariationSelector
A Springham
6
Achtung, ganze Zahlen 0-9 gelten als Emojis. Damit"6".unicodeScalars.first!.properties.isEmoji wird bewertet alstrue
Miniroo
6

Mit Swift 5 können Sie jetzt die Unicode-Eigenschaften jedes Zeichens in Ihrer Zeichenfolge überprüfen. Dies gibt uns die bequeme isEmojiVariable für jeden Buchstaben. Das Problem ist isEmoji, dass true für jedes Zeichen zurückgegeben wird, das in ein 2-Byte-Emoji konvertiert werden kann, z. B. 0-9.

Wir können uns die Variable ansehen isEmojiund auch prüfen, ob ein Emoji-Modifikator vorhanden ist, um festzustellen, ob die mehrdeutigen Zeichen als Emoji angezeigt werden.

Diese Lösung sollte viel zukunftssicherer sein als die hier angebotenen Regex-Lösungen.

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

Geben uns

"hey".containsEmoji() //false

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

"3".containsEmoji() //false
"3️⃣".containsEmoji() //true
Miniroo
quelle
1
Und was mehr ist, Character("3️⃣").isEmoji // truewährendCharacter("3").isEmoji // false
Paul B
4

Swift 3 Hinweis:

Es scheint, dass die cnui_containsEmojiCharactersMethode entweder entfernt oder in eine andere dynamische Bibliothek verschoben wurde. _containsEmojisollte aber trotzdem funktionieren.

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:

Ich habe kürzlich eine private API entdeckt, auf NSStringder Funktionen zum Erkennen, ob eine Zeichenfolge ein Emoji-Zeichen enthält, verfügbar gemacht werden:

let str: NSString = "hello😊"

Mit einem Objektprotokoll und unsafeBitCast:

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

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

Mit valueForKey:

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

Bei einer reinen Swift-Saite müssen Sie die Saite wie AnyObjectfolgt umwandeln valueForKey:

let str = "hello😊"

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

In der NSString-Headerdatei gefundene Methoden .

JAL
quelle
Dies ist, was ich suche, danke JAL
Wird dies von Apple abgelehnt?
Andrey Chernukha
@AndreyChernukha Es gibt immer ein Risiko, aber ich habe noch keine Ablehnung erfahren.
JAL
Verwenden Sie niemals private APIs. Bestenfalls wird der Schmerz erst morgen kommen. Oder nächsten Monat.
Xaphod
3

Sie können diesen Code verwenden Beispiel oder diese Schote .

Um es in Swift zu verwenden, importieren Sie die Kategorie in die YourProject_Bridging_Header

#import "NSString+EMOEmoji.h"

Dann können Sie den Bereich für jedes Emoji in Ihrem String überprüfen:

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

let containsEmoji: Bool = example.emo_containsEmoji()

    print(containsEmoji)

// Output: ["true"]

Ich habe ein kleines Beispielprojekt mit dem obigen Code erstellt.

Gabriel.Massana
quelle
3

Zukunftssicher: Überprüfen Sie die Pixel des Charakters manuell. Die anderen Lösungen brechen (und sind kaputt gegangen), wenn neue Emojis hinzugefügt werden.

Hinweis: Dies ist Objective-C (kann in Swift konvertiert werden)

Im Laufe der Jahre brechen diese Emoji-Erkennungslösungen immer wieder ab, da Apple neue Emojis mit neuen Methoden hinzufügt (wie hautfarbene Emojis, die durch Vorfluchen eines Zeichens mit einem zusätzlichen Zeichen erstellt wurden) usw.

Ich bin schließlich zusammengebrochen und habe gerade die folgende Methode geschrieben, die für alle aktuellen Emojis funktioniert und für alle zukünftigen Emojis funktionieren sollte.

Die Lösung erstellt ein UILabel mit dem Zeichen und einem schwarzen Hintergrund. CG macht dann einen Schnappschuss des Etiketts und ich scanne alle Pixel im Schnappschuss nach nicht durchgehend schwarzen Pixeln. Der Grund, warum ich den schwarzen Hintergrund hinzufüge, besteht darin, Probleme mit Falschfarben aufgrund von Subpixel-Rendering zu vermeiden

Die Lösung läuft auf meinem Gerät SEHR schnell, ich kann Hunderte von Zeichen pro Sekunde überprüfen, aber es sollte beachtet werden, dass dies eine CoreGraphics-Lösung ist und nicht so stark verwendet werden sollte, wie Sie es mit einer normalen Textmethode könnten. Die Grafikverarbeitung ist datenintensiv, sodass das gleichzeitige Überprüfen von Tausenden von Zeichen zu einer spürbaren Verzögerung führen kann.

-(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;
    
}
Albert Renshaw
quelle
4
Ich mag dein Denken! ;) - Out of the Box!
Ramon
Warum tust du uns das an? #apple #unicodestandard 😱🤔🤪🙈😈🤕💩
d4Rk
Ich habe mir das schon eine Weile nicht mehr angesehen, aber ich frage mich, ob ich dann zu UIColor und dann zu hsb konvertieren muss. es scheint, ich kann nur überprüfen, ob r, g, b alle == 0? Wenn jemand versucht, lassen Sie es mich wissen
Albert Renshaw
Ich mag diese Lösung, aber wird sie nicht mit einem Charakter wie ℹ brechen?
Juan Carlos Ospina Gonzalez
1
@JuanCarlosOspinaGonzalez Nein, in Emoji, das als blaue Box mit einem weißen i gerendert wird. Es bringt jedoch einen guten Punkt zum Ausdruck, dass das UILabel die Schriftart erzwingen sollte AppleColorEmoji, und fügt dies jetzt als ausfallsicher hinzu, obwohl ich denke, dass Apple es sowieso für diese festlegen wird
Albert Renshaw
2

Für Swift 3.0.2 ist die folgende Antwort die einfachste:

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;
}
Ankit Goyal
quelle
2

Die absolut ähnliche Antwort zu denen, die vor mir geschrieben haben, aber mit aktualisierten Emoji-Skalaren.

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

}
Alex Shoshiashvili
quelle
0

Für die genannte Aufgabe gibt es eine gute Lösung . Das Überprüfen von Unicode.Scalar.Properties von Unicode-Skalaren ist jedoch für ein einzelnes Zeichen gut. Und nicht flexibel genug für Strings.

Wir können stattdessen reguläre Ausdrücke verwenden - universellerer Ansatz. Im Folgenden finden Sie eine detaillierte Beschreibung der Funktionsweise. Und hier geht die Lösung.

Die Lösung

In Swift können Sie mithilfe einer Erweiterung mit einer solchen berechneten Eigenschaft überprüfen, ob ein String ein einzelnes Emoji-Zeichen ist:

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
    }

}

Wie es funktioniert (im Detail)

Ein einzelnes Emoji (eine Glyphe) kann durch eine Reihe verschiedener Symbole, Sequenzen und deren Kombinationen reproduziert werden. Unicode-Spezifikation definiert mehrere mögliche Emoji-Zeichendarstellungen.

Einzelzeichen Emoji

Ein Emoji-Zeichen, das von einem einzelnen Unicode-Skalar reproduziert wird.

Unicode definiert Emoji Character als:

emoji_character := \p{Emoji}

Dies bedeutet jedoch nicht unbedingt, dass ein solcher Charakter als Emoji gezeichnet wird. Bei einem gewöhnlichen numerischen Symbol „1“ ist die Emoji-Eigenschaft wahr, obwohl sie möglicherweise immer noch als Text gezeichnet wird. Und es gibt eine Liste solcher Symbole: #, ©, 4 usw.

Man sollte denken, dass wir zusätzliche Eigenschaften verwenden können, um zu überprüfen: "Emoji_Presentation". Aber so funktioniert es nicht. Es gibt ein Emoji wie 🏟 oder 🛍, die die Eigenschaft Emoji_Presentation = false haben.

Um sicherzustellen, dass das Zeichen standardmäßig als Emoji gezeichnet wird, sollten wir seine Kategorie überprüfen: Es sollte "Other_symbol" sein.

Tatsächlich sollte der reguläre Ausdruck für Emoji mit einem Zeichen wie folgt definiert werden:

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

Emoji-Präsentationssequenz

Ein Zeichen, das normalerweise entweder als Text oder als Emoji gezeichnet werden kann. Das Erscheinungsbild hängt von einem speziellen folgenden Symbol ab, einem Präsentationswähler, der den Präsentationstyp angibt. \ x {FE0E} definiert die Textdarstellung. \ x {FE0F} definiert die Emoji-Darstellung.

Die Liste solcher Symbole finden Sie [hier] (
 https://unicode.org/Public/emoji/12.1/emoji-variation-sequences.txt ).

Unicode definiert die Präsentationssequenz wie folgt:

emoji_presentation_sequence := emoji_character emoji_presentation_selector

Reguläre Expressionssequenz dafür:

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

Emoji Keycap-Sequenz

Die Sequenz sieht der Präsentationssequenz sehr ähnlich, hat jedoch am Ende einen zusätzlichen Skalar: \ x {20E3}. Der Umfang der möglichen Basisskalare, die dafür verwendet werden, ist ziemlich eng: 0-9 # * - und das ist alles. Beispiele: 1️⃣, 8️⃣, * ️⃣.

Unicode definiert die Tastenkappenfolge wie folgt:

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

Regulärer Ausdruck dafür:

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

Emoji-Modifikator-Sequenz

Einige Emojis können ein verändertes Aussehen wie ein Hautton haben. Zum Beispiel kann Emoji 🧑 anders sein: 🧑🧑🏻🧑🏼🧑🏽🧑🏾🧑🏿. Um ein Emoji zu definieren, das in diesem Fall "Emoji_Modifier_Base" heißt, kann ein nachfolgender "Emoji_Modifier" verwendet werden.

Im Allgemeinen sieht eine solche Sequenz folgendermaßen aus:

emoji_modifier_sequence := emoji_modifier_base emoji_modifier

Um dies zu erkennen, können wir nach einer Sequenz mit regulären Ausdrücken suchen:

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

Emoji-Flaggensequenz

Flaggen sind Emojis mit ihrer besonderen Struktur. Jedes Flag wird mit zwei Symbolen „Regional_Indicator“ dargestellt.

Unicode definiert sie wie folgt:

emoji_flag_sequence := regional_indicator regional_indicator

Zum Beispiel wird die Flagge der Ukraine 🇺🇦 tatsächlich mit zwei Skalaren dargestellt: \ u {0001F1FA \ u {0001F1E6}

Regulärer Ausdruck dafür:

emoji_flag_sequence := \p{RI}{2}

Emoji-Tag-Sequenz (ETS)

Eine Sequenz, die eine sogenannte tag_base verwendet, gefolgt von einer benutzerdefinierten Tag-Spezifikation, die aus einem Bereich von Symbolen \ x {E0020} - \ x {E007E} besteht und durch tag_end mark \ x {E007F} abgeschlossen wird.

Unicode definiert es folgendermaßen:

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}

Seltsamerweise erlaubt Unicode, dass das Tag in ED-14a auf emoji_modifier_sequence oder emoji_presentation_sequence basiert . Gleichzeitig scheinen sie in regulären Ausdrücken, die in derselben Dokumentation bereitgestellt werden , die Reihenfolge nur anhand eines einzelnen Emoji-Zeichens zu überprüfen.

In der Unicode 12.1 Emoji-Liste gibt es nur drei solcher Emojis definiert. Alle von ihnen sind Flaggen der britischen Länder: England, Schottland und Wales. Und alle basieren auf einem einzigen Emoji-Charakter. Wir sollten also nur nach einer solchen Sequenz suchen.

Regulären Ausdruck:

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

Emoji Zero-Width Joiner Sequence (ZWJ-Sequenz)

Ein Joiner mit einer Breite von Null ist ein Skalar \ x {200D}. Mit seiner Hilfe können mehrere Charaktere, die bereits Emojis sind, zu neuen kombiniert werden.

Zum Beispiel wird eine „Familie mit Vater, Sohn und Tochter“ Emoji 👨‍👧‍👦 durch eine Kombination von Vater 👨, Tochter 👧 und Sohn 👦 Emojis reproduziert, die mit ZWJ-Symbolen zusammengeklebt sind.

Es ist zulässig, Elemente zusammenzuhalten, bei denen es sich um einzelne Emoji-Zeichen, Präsentations- und Modifikatorsequenzen handelt.

Der reguläre Ausdruck für eine solche Sequenz sieht im Allgemeinen folgendermaßen aus:

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

Regulärer Ausdruck für alle

Alle oben genannten Emoji-Darstellungen können durch einen einzigen regulären Ausdruck beschrieben werden:

\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}] 
    ) 
  )*
Dmytro Babych
quelle
-1

Ich hatte das gleiche Problem und machte am Ende eine Stringund CharacterErweiterungen.

Der Code ist zu lang, um ihn zu veröffentlichen, da er tatsächlich alle Emojis (aus der offiziellen Unicode-Liste v5.0) auflistet. CharacterSetSie finden ihn hier:

https://github.com/piterwilson/StringEmoji

Konstanten

let emojiCharacterSet: CharacterSet

Zeichensatz mit allen bekannten Emoji (wie in der offiziellen Unicode-Liste 5.0 beschrieben http://unicode.org/emoji/charts-5.0/emoji-list.html )

String

var isEmoji: Bool {get}

Gibt an, ob die StringInstanz ein bekanntes einzelnes Emoji-Zeichen darstellt

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

Gibt an, ob die StringInstanz ein bekanntes Emoji-Zeichen enthält

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

Wendet ein kCFStringTransformToUnicodeName- CFStringTransformauf eine Kopie des Strings an

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

Gibt das Ergebnis von a kCFStringTransformToUnicodeName- zurück, CFStringTransformwobei \N{Präfixe und }Suffixe entfernt wurden

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

Charakter

var isEmoji: Bool {get}

Gibt an, ob die CharacterInstanz ein bekanntes Emoji-Zeichen darstellt

print("".isEmoji) // false
print("😁".isEmoji) // true
Juan Carlos Ospina Gonzalez
quelle