¿Averigua si Character in String es emoji?

90

Necesito averiguar si un personaje de una cadena es un emoji.

Por ejemplo, tengo este personaje:

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

Necesito averiguar si ese personaje es un emoji.

Andrés
fuente
Tengo curiosidad: ¿por qué necesitas esa información?
Martin R
@EricD .: Hay muchos caracteres Unicode que toman más de un punto de código UTF-8 (por ejemplo, "€" = E2 82 AC) o más de un punto de código UTF-16 (por ejemplo, "𝄞" = D834 DD1E).
Martin R
Espero que tengas una idea de esta versión obj-c del código stackoverflow.com/questions/19886642/…
Ashish Kakkad
Las cadenas tienen su indexación, que es la forma preferida de usarlas. Para obtener un carácter en particular (o un grupo de grafemas más bien), puede: let character = string[string.index(after: string.startIndex)]o let secondCharacter = string[string.index(string.startIndex, offsetBy: 1)]
Paul B

Respuestas:

228

Lo que encontré es la diferencia entre caracteres, escalares Unicode y glifos.

Por ejemplo, el glifo 👨‍👨‍👧‍👧 consta de 7 escalares Unicode:

Otro ejemplo, el glifo 👌🏿 consta de 2 escalares Unicode:

  • El emoji normal: 👌
  • Un modificador del tono de piel: 🏿

El último, el glifo 1️⃣ contiene tres caracteres Unicode:

Entonces, al renderizar los caracteres, los glifos resultantes realmente importan.

Swift 5.0 y superior facilita mucho este proceso y elimina algunas conjeturas que teníamos que hacer. Unicode.ScalarEl nuevo Propertytipo ayuda a determinar a qué nos enfrentamos. Sin embargo, esas propiedades solo tienen sentido cuando se verifican los otros escalares dentro del glifo. Es por eso que agregaremos algunos métodos convenientes a la clase Character para ayudarnos.

Para obtener más detalles, escribí un artículo que explica cómo funciona esto .

Para Swift 5.0, te deja con el siguiente resultado:

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

Lo que te dará los siguientes resultados:

"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

Para versiones anteriores de Swift, consulte esta esencia que contiene mi código anterior.

Kevin R.
fuente
6
Esta es, con mucho, la mejor y más correcta respuesta aquí. ¡Gracias! Una pequeña nota, sus ejemplos no coinciden con el código (cambió el nombre de containsOnlyEmoki a containsEmoji en el fragmento; supongo que porque es más correcto, en mis pruebas devolvió verdadero para cadenas con caracteres mixtos).
Tim Bull
3
Mi mal, cambié un código, supongo que lo arruiné. Actualicé el ejemplo
Kevin R
2
@Andrew: Claro, agregué otro método al ejemplo para demostrar esto :).
Kevin R
2
@ Andrew, aquí es donde se pone realmente complicado. Agregué un ejemplo de cómo hacer eso. El problema es que supuse saber cómo CoreText representará los glifos simplemente verificando los caracteres. Si alguien tiene sugerencias para un método más limpio, hágamelo saber.
Kevin R
3
@Andrew Gracias por señalar eso, cambié la forma de containsOnlyEmojiverificaciones. También actualicé el ejemplo a Swift 3.0.
Kevin R
48

La forma más simple, limpia y rápida de lograr esto es simplemente verificar los puntos de código Unicode para cada carácter en la cadena con rangos conocidos de emoji y dingbats, así:

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
fuente
9
Un ejemplo de código como este es mucho mejor que sugerir incluir una dependencia de biblioteca de terceros. La respuesta de Shardul es un consejo imprudente a seguir: escriba siempre su propio código.
thefaj
Esto es genial, gracias por comentar a qué se refieren los casos
Shawn Throop
1
Como tanto su código, lo implementé en una respuesta aquí . Una cosa que noté es que faltan algunos emoji, tal vez porque no son parte de las categorías que enumeraste, por ejemplo, esta: Emoji de cara de robot 🤖
Cue
1
@Tel Supongo que sería el rango 0x1F900...0x1F9FF(según Wikipedia). No estoy seguro de que todo el rango deba considerarse 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
    }
}

Esta es mi solución, con rangos actualizados.

Sebastián López
fuente
8

Swift 5.0

... introdujo una nueva forma de comprobar exactamente esto.

Tienes que romper tu Stringen su Scalars. ¡Cada uno Scalartiene un Propertyvalor que respalda el isEmojivalor!

De hecho, incluso puedes comprobar si el escalar es un modificador de Emoji o más. Consulte la documentación de Apple: https://developer.apple.com/documentation/swift/unicode/scalar/properties

Es posible que desee considerar la verificación en isEmojiPresentationlugar de isEmoji, porque Apple establece lo siguiente para isEmoji:

Esta propiedad es válida para los escalares que se representan como emoji de forma predeterminada y también para los escalares que tienen una representación de emoji no predeterminada cuando son seguidos por U + FE0F VARIATION SELECTOR-16. Esto incluye algunos escalares que normalmente no se consideran emoji.


De esta manera, los Emoji se dividen en todos los modificadores, pero es mucho más sencillo de manejar. Y como Swift ahora cuenta los Emoji con modificadores (por ejemplo: 👨‍👩‍👧‍👦, 👨🏻‍💻, 🏴) como 1, puedes hacer todo tipo de cosas.

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 señala una forma interesante de obtener todos los 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)
    }
}
alexkaessner
fuente
1
Gran respuesta, gracias. Vale la pena mencionar que su min sdk debe ser 10.2 para usar esta parte de Swift 5. Además, para verificar si una cadena solo estaba compuesta por emojis, tuve que verificar si tenía una de estas propiedades:scalar.properties.isEmoji scalar.properties.isEmojiPresentation scalar.properties.isEmojiModifier scalar.properties.isEmojiModifierBase scalar.properties.isJoinControl scalar.properties.isVariationSelector
Un Springham
6
Cuidado, los números enteros del 0 al 9 se consideran emojis. Así "6".unicodeScalars.first!.properties.isEmojise evaluará comotrue
Miniroo
6

Con Swift 5 ahora puede inspeccionar las propiedades Unicode de cada carácter en su cadena. Esto nos da la isEmojivariable conveniente en cada letra. El problema es isEmojique devolverá verdadero para cualquier carácter que se pueda convertir en un emoji de 2 bytes, como 0-9.

Podemos mirar la variable isEmojiy también verificar la presencia de un modificador de emoji para determinar si los caracteres ambiguos se mostrarán como un emoji.

Esta solución debería ser mucho más preparada para el futuro que las soluciones de expresiones regulares que se ofrecen aquí.

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

Dándonos

"hey".containsEmoji() //false

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

"3".containsEmoji() //false
"3️⃣".containsEmoji() //true
Miniroo
fuente
1
Y lo que es más es Character("3️⃣").isEmoji // truemientrasCharacter("3").isEmoji // false
Paul B
4

Swift 3 Nota:

Parece que el cnui_containsEmojiCharactersmétodo se eliminó o se movió a una biblioteca dinámica diferente. _containsEmojiSin embargo, aún debería funcionar.

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

Rápido 2.x:

Recientemente descubrí una API privada en la NSStringque expone la funcionalidad para detectar si una cadena contiene un carácter Emoji:

let str: NSString = "hello😊"

Con un protocolo objc y 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 cuerda Swift pura, debes lanzar la cuerda como AnyObjectantes de usar valueForKey:

let str = "hello😊"

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

Métodos encontrados en el archivo de encabezado NSString .

JAL
fuente
Esto es lo que estoy buscando, gracias JAL
¿Será esto rechazado por Apple?
Andrey Chernukha
@AndreyChernukha Siempre existe un riesgo, pero todavía no he experimentado ningún rechazo.
JAL
Nunca uses API privadas. En el mejor de los casos, el dolor solo llegará mañana. O el mes que viene.
xaphod
3

Puede utilizar este ejemplo de código o este pod .

Para usarlo en Swift, importe la categoría en el YourProject_Bridging_Header

#import "NSString+EMOEmoji.h"

Luego, puede verificar el rango de cada emoji en su Cadena:

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

let containsEmoji: Bool = example.emo_containsEmoji()

    print(containsEmoji)

// Output: ["true"]

Creé un pequeño proyecto de ejemplo con el código anterior.

Gabriel.Massana
fuente
3

Prueba de futuro: comprueba manualmente los píxeles del personaje; las otras soluciones se romperán (y se habrán roto) a medida que se agreguen nuevos emojis.

Nota: Esto es Objective-C (se puede convertir a Swift)

A lo largo de los años, estas soluciones de detección de emojis se siguen rompiendo a medida que Apple agrega nuevos emojis con nuevos métodos (como emojis con tonos de piel creados al maldecir previamente un personaje con un personaje adicional), etc.

Finalmente rompí y escribí el siguiente método que funciona para todos los emojis actuales y debería funcionar para todos los emojis futuros.

La solución crea una UILabel con el personaje y un fondo negro. CG luego toma una instantánea de la etiqueta y yo escaneo todos los píxeles en la instantánea en busca de píxeles que no sean negros sólidos. La razón por la que agrego el fondo negro es para evitar problemas de coloración falsa debido a representación de subpíxeles

La solución se ejecuta MUY rápido en mi dispositivo, puedo verificar cientos de caracteres por segundo, pero debe tenerse en cuenta que esta es una solución de CoreGraphics y no debe usarse mucho como lo haría con un método de texto normal. El procesamiento de gráficos tiene muchos datos, por lo que la verificación de miles de caracteres a la vez podría provocar un retraso 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;
    
}
Albert Renshaw
fuente
4
¡Me gusta tu forma de pensar! ;) - ¡Fuera de la caja!
Ramon
¿Por qué nos haces esto? #apple #unicodestandard 😱🤔🤪🙈😈🤕💩
d4Rk
No he mirado esto en un tiempo pero me pregunto si tengo que convertir a UIColor y luego a hsb; parece que puedo comprobar que r, g, b todos == 0? Si alguien intenta, avíseme
Albert Renshaw
Me gusta esta solución, pero ¿no romperá con un personaje como ℹ?
Juan Carlos Ospina Gonzalez
1
@JuanCarlosOspinaGonzalez Nope, en emoji que se muestra como un cuadro azul con una i blanca. Sin embargo, trae un buen punto que UILabel debería forzar la fuente AppleColorEmoji, agregando que ahora es a prueba de fallas, aunque creo que Apple lo usará por defecto para esos de todos modos
Albert Renshaw
2

Para Swift 3.0.2, la siguiente respuesta es la más 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;
}
Ankit Goyal
fuente
2

La respuesta absolutamente similar a las que escribieron antes que yo, pero con un conjunto actualizado de escalares 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
        }
    }

}
Alex Shoshiashvili
fuente
0

Existe una buena solución para la tarea mencionada. Pero comprobar Unicode.Scalar.Properties de escalares Unicode es bueno para un solo carácter. Y no lo suficientemente flexible para Strings.

Podemos usar expresiones regulares en su lugar , un enfoque más universal. Hay una descripción detallada de cómo funciona a continuación. Y aquí va la solución.

La solución

En Swift, puede verificar si una cadena es un solo carácter Emoji, usando una extensión con una propiedad calculada de este tipo:

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
    }

}

Cómo funciona (en detalles)

Un solo Emoji (un glifo) se puede reproducir mediante varios símbolos, secuencias y combinaciones diferentes. Especificación Unicode define varias posibles representaciones de caracteres Emoji.

Emoji de un solo carácter

Un carácter Emoji reproducido por un único escalar Unicode.

Unicode define el carácter Emoji como:

emoji_character := \p{Emoji}

Pero no significa necesariamente que ese personaje se dibujará como un Emoji. Un símbolo numérico ordinario "1" tiene la propiedad Emoji siendo verdadera, aunque aún podría dibujarse como texto. Y hay una lista de tales símbolos: #, ©, 4, etc.

Uno debería pensar que podemos usar una propiedad adicional para verificar: “Emoji_Presentation”. Pero no funciona así. Hay un Emoji como 🏟 o 🛍, que tienen la propiedad Emoji_Presentation = false.

Para asegurarnos de que el personaje se dibuja como Emoji por defecto, debemos marcar su categoría: debe ser "Otro_símbolo".

Entonces, de hecho, la expresión regular para Emoji de un solo carácter debe definirse como:

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

Secuencia de presentación de emoji

Un personaje, que normalmente se puede dibujar como texto o como Emoji. Su apariencia depende de un siguiente símbolo especial, un selector de presentación, que indica su tipo de presentación. \ x {FE0E} define la representación del texto. \ x {FE0F} define la representación de emoji.

La lista de tales símbolos se puede encontrar [aquí] (
 https://unicode.org/Public/emoji/12.1/emoji-variation-sequences.txt ).

Unicode define la secuencia de presentación de esta manera:

emoji_presentation_sequence := emoji_character emoji_presentation_selector

Secuencia de expresión regular para ello:

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

Secuencia de teclas de Emoji

La secuencia se parece mucho a la secuencia de presentación, pero tiene un escalar adicional al final: \ x {20E3}. El alcance de los posibles escalares base utilizados para ello es bastante estrecho: 0-9 # * - y eso es todo. Ejemplos: 1️⃣, 8️⃣, * ️⃣.

Unicode define la secuencia de teclas de esta manera:

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

Expresión regular para ello:

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

Secuencia del modificador de emoji

Algunos emojis pueden tener una apariencia modificada, como un tono de piel. Por ejemplo, Emoji 🧑 puede ser diferente: 🧑🧑🏻🧑🏼🧑🏽🧑🏾🧑🏿. Para definir un Emoji, que se llama "Emoji_Modifier_Base" en este caso, se puede utilizar un "Emoji_Modifier" posterior.

En general, dicha secuencia se ve así:

emoji_modifier_sequence := emoji_modifier_base emoji_modifier

Para detectarlo podemos buscar una secuencia de expresión regular:

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

Secuencia de banderas emoji

Las banderas son emojis con su estructura particular. Cada bandera está representada con dos símbolos “Regional_Indicator”.

Unicode los define como:

emoji_flag_sequence := regional_indicator regional_indicator

Por ejemplo, la bandera de Ucrania 🇺🇦 de hecho se representa con dos escalares: \ u {0001F1FA \ u {0001F1E6}

Expresión regular para ello:

emoji_flag_sequence := \p{RI}{2}

Secuencia de etiquetas de emoji (ETS)

Una secuencia que utiliza la denominada base de etiquetas, que va seguida de una especificación de etiqueta personalizada compuesta por un rango de símbolos \ x {E0020} - \ x {E007E} y concluida por una marca de fin de etiqueta \ x {E007F}.

Unicode lo define así:

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}

Lo extraño es que Unicode permite que la etiqueta se base en emoji_modifier_sequence o emoji_presentation_sequence en ED-14a . Pero al mismo tiempo, en las expresiones regulares proporcionadas en la misma documentación , parecen verificar la secuencia basándose en un solo carácter Emoji.

En la lista de Emojis Unicode 12.1, solo hay tres de estos Emojis definidos. Todas ellas son banderas de los países del Reino Unido: Inglaterra 🏴󠁧󠁢󠁥󠁮󠁧󠁿, Escocia 🏴󠁧󠁢󠁳󠁣󠁴󠁿 y Gales 🏴󠁧󠁢󠁷󠁬󠁳󠁿. Y todos ellos se basan en un solo personaje Emoji. Entonces, será mejor que verifiquemos solo esa secuencia.

Expresión regular:

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

Secuencia de unión de ancho cero de Emoji (secuencia ZWJ)

Un ensamblador de ancho cero es un escalar \ x {200D}. Con su ayuda, varios personajes, que ya son Emojis por sí mismos, se pueden combinar en otros nuevos.

Por ejemplo, un emoji 👨‍👧‍👦 de “familia con padre, hijo e hija” se reproduce mediante una combinación de emojis de padre 👨, hija 👧 e hijo 👦 pegados con símbolos ZWJ.

Se permite unir elementos, que son caracteres de un solo emoji, secuencias de presentación y modificadores.

La expresión regular para dicha secuencia en general se ve así:

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

Expresión regular para todos ellos

Todas las representaciones de Emoji mencionadas anteriormente se pueden describir mediante una sola expresión regular:

\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
fuente
-1

Tuve el mismo problema y terminé haciendo extensiones Stringy Character.

El código es demasiado largo para publicarlo, ya que en realidad enumera todos los emojis (de la lista oficial de Unicode v5.0) en un CharacterSetpuedes encontrarlo aquí:

https://github.com/piterwilson/StringEmoji

Constantes

let emojiCharacterSet: CharacterSet

Conjunto de caracteres que contiene todos los emoji conocidos (como se describe en la lista oficial Unicode 5.0 http://unicode.org/emoji/charts-5.0/emoji-list.html )

Cuerda

var isEmoji: Bool {get}

Si la Stringinstancia representa o no un único carácter emoji conocido

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

Si la Stringinstancia contiene o no un carácter emoji conocido

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

Aplica un kCFStringTransformToUnicodeName- CFStringTransformen una copia de la Cadena

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

Devuelve el resultado de a kCFStringTransformToUnicodeName- CFStringTransformcon \N{prefijos y }sufijos eliminados

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

Personaje

var isEmoji: Bool {get}

Si la Characterinstancia representa o no un carácter emoji conocido

print("".isEmoji) // false
print("😁".isEmoji) // true
Juan Carlos Ospina González
fuente