Descobrir se Character in String é emoji?

92

Preciso descobrir se um caractere em uma string é um emoji.

Por exemplo, tenho este personagem:

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

Preciso descobrir se esse personagem é um emoji.

Andrew
fonte
Estou curioso: por que você precisa dessa informação?
Martin R de
@EricD .: Existem muitos caracteres Unicode que levam mais de um ponto de código UTF-8 (por exemplo, "€" = E2 82 AC) ou mais de um ponto de código UTF-16 (por exemplo, "𝄞" = D834 DD1E).
Martin R de
Espero que você tenha uma ideia com esta versão obj-c do código stackoverflow.com/questions/19886642/…
Ashish Kakkad
Strings têm sua indexação, que é a forma preferida de usá-los. Para obter um caractere específico (ou agrupamento de grafemas), você pode: let character = string[string.index(after: string.startIndex)]ou let secondCharacter = string[string.index(string.startIndex, offsetBy: 1)]
Paul B

Respostas:

239

O que descobri é a diferença entre caracteres, escalares Unicode e glifos.

Por exemplo, o glifo 👨‍👨‍👧‍👧 consiste em 7 escalares Unicode:

Outro exemplo, o glifo 👌🏿 consiste em 2 escalares Unicode:

  • O emoji normal: 👌
  • Um modificador de tom de pele: 🏿

Por último, o glifo 1️⃣ contém três caracteres Unicode:

Portanto, ao renderizar os caracteres, os glifos resultantes são realmente importantes.

O Swift 5.0 e superior tornam esse processo muito mais fácil e elimina algumas suposições que precisávamos fazer. Unicode.Scalaré novoProperty tipo de ajuda é determinar com o que estamos lidando. No entanto, essas propriedades só fazem sentido ao verificar os outros escalares dentro do glifo. É por isso que adicionaremos alguns métodos de conveniência à classe Character para nos ajudar.

Para obter mais detalhes, escrevi um artigo explicando como isso funciona .

Para Swift 5.0, ele deixa você com o seguinte 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 } }
}

O que lhe dará os seguintes 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 versões mais antigas do Swift, verifique esta essência que contém meu código antigo.

Kevin R
fonte
6
Esta é de longe a melhor e mais correta resposta aqui. Obrigado! Uma pequena observação, seus exemplos não correspondem ao código (você renomeou containsOnlyEmoki para containsEmoji no snippet - presumo porque é mais correto, em meu teste retornou true para strings com caracteres mistos).
Tim Bull
3
Meu mal, mudei algum código, acho que errei. Eu atualizei o exemplo
Kevin R
2
@Andrew: Claro, adicionei outro método ao exemplo para demonstrar isso :).
Kevin R
2
@Andrew é aqui que fica tudo muito complicado. Eu adicionei um exemplo de como fazer isso. O problema é que assumi saber como o CoreText renderizará os glifos simplesmente verificando os caracteres. Se alguém tiver sugestões para um método mais limpo, por favor me avise.
Kevin R
3
@Andrew Obrigado por apontar isso, mudei a forma de containsOnlyEmojiverificação. Eu também atualizei o exemplo para Swift 3.0.
Kevin R
51

A maneira mais simples, limpa e rápida de fazer isso é simplesmente verificar os pontos de código Unicode para cada caractere na string em relação aos intervalos de emoji e dingbats conhecidos, como:

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
fonte
9
Um exemplo de código como este é muito melhor do que sugerir incluir uma dependência de biblioteca de terceiros. A resposta de Shardul é um conselho imprudente a seguir - sempre escreva seu próprio código.
thefaj
Isso é ótimo, obrigado por comentar a que se referem os casos
Shawn Throop
2
Gostei muito do seu código, eu o implementei em uma resposta aqui . Uma coisa que notei é que faltam alguns emojis, talvez porque eles não fazem parte das categorias que você listou, por exemplo este: Emoji de cara de robô 🤖
Dica
1
@Tel eu acho que seria o intervalo 0x1F900...0x1F9FF(por Wikipedia). Não tenho certeza se todo o intervalo deve ser considerado emoji.
Frizlab de
11

Swift 5.0

… Introduziu uma nova forma de verificar exatamente isso!

Você tem que quebrar o Stringseu Scalars. Cada Scalarum tem um Propertyvalor que suporta o isEmojivalor!

Na verdade, você pode até verificar se o escalar é um modificador de Emoji ou mais. Verifique a documentação da Apple: https://developer.apple.com/documentation/swift/unicode/scalar/properties

Você pode querer considerar verificar em isEmojiPresentationvez de isEmoji, porque a Apple afirma o seguinte para isEmoji:

Esta propriedade é verdadeira para escalares que são renderizados como emoji por padrão e também para escalares que têm uma renderização de emoji não padrão quando seguido por U + FE0F VARIATION SELECTOR-16. Isso inclui alguns escalares que normalmente não são considerados emoji.


Dessa forma, na verdade, os Emojis são divididos em todos os modificadores, mas é mais simples de manusear. E como o Swift agora conta os Emoji com modificadores (por exemplo: 👨‍👩‍👧‍👦, 👨🏻‍💻, 🏴) como 1, você pode fazer todo o tipo de coisas.

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 aponta uma maneira interessante de obter todos os emojis:

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
fonte
1
Ótima resposta, obrigado. Vale a pena mencionar que seu min sdk deve ser 10,2 para usar esta parte do Swift 5. Também para verificar se uma string era composta apenas de emojis, tive que verificar se ela tinha uma das seguintes propriedades:scalar.properties.isEmoji scalar.properties.isEmojiPresentation scalar.properties.isEmojiModifier scalar.properties.isEmojiModifierBase scalar.properties.isJoinControl scalar.properties.isVariationSelector
A Springham
6
Cuidado, números inteiros de 0 a 9 são considerados emojis. Então "6".unicodeScalars.first!.properties.isEmojivai avaliar comotrue
Miniroo
1
Existem outros caracteres semelhantes #e *que também retornarão verdadeiro para o isEmojicheque. isEmojiPresentationparece funcionar melhor, pelo menos ele retorna falsepara 0...9, #, *e qualquer outro símbolo que eu poderia tentar em um teclado Inglês-US. Alguém tem mais experiência com ele e sabe se ele é confiável para validação de entrada?
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
    }
}

Esta é a minha correção, com intervalos atualizados.

Sebastian lopez
fonte
7

Com o Swift 5 agora você pode inspecionar as propriedades unicode de cada caractere em sua string. Isso nos dá a isEmojivariável conveniente em cada letra. O problema é isEmojique retornará verdadeiro para qualquer caractere que possa ser convertido em um emoji de 2 bytes, como 0-9.

Podemos olhar para a variável isEmojie também verificar a presença de um modificador de emoji para determinar se os caracteres ambíguos serão exibidos como um emoji.

Esta solução deve ser muito mais preparada para o futuro do que as soluções regex oferecidas aqui.

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

Dando-nos

"hey".containsEmoji() //false

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

"3".containsEmoji() //false
"3️⃣".containsEmoji() //true
Miniroo
fonte
1
E o que é mais é Character("3️⃣").isEmoji // trueenquantoCharacter("3").isEmoji // false
Paul B
4

Swift 3 Nota:

Parece que o cnui_containsEmojiCharactersmétodo foi removido ou movido para uma biblioteca dinâmica diferente. _containsEmojiainda deve funcionar embora.

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, descobri uma API privada na NSStringqual expõe a funcionalidade para detectar se uma string contém um caractere Emoji:

let str: NSString = "hello😊"

Com um protocolo 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

Com valueForKey:

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

Com uma string Swift pura, você deve lançar a string como AnyObjectantes de usar valueForKey:

let str = "hello😊"

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

Métodos encontrados no arquivo de cabeçalho NSString .

JAL
fonte
Isso é o que estou procurando, obrigado JAL
Isso será rejeitado pela Apple?
Andrey Chernukha
@AndreyChernukha Sempre existe um risco, mas ainda não experimentei nenhuma rejeição.
JAL
Nunca, jamais, use APIs privadas. Na melhor das hipóteses, a dor só virá amanhã. Ou no próximo mês.
xaphod
3

Você pode usar este exemplo de código ou este pod .

Para usá-lo no Swift, importe a categoria para o YourProject_Bridging_Header

#import "NSString+EMOEmoji.h"

Em seguida, você pode verificar o intervalo de cada emoji em sua String:

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

let containsEmoji: Bool = example.emo_containsEmoji()

    print(containsEmoji)

// Output: ["true"]

Criei um pequeno projeto de exemplo com o código acima.

Gabriel.Massana
fonte
3

Prova Futura: Verifique manualmente os pixels do personagem; as outras soluções irão falhar (e quebraram) conforme novos emojis são adicionados.

Nota: Este é Objective-C (pode ser convertido para Swift)

Com o passar dos anos, essas soluções de detecção de emojis continuam surgindo à medida que a Apple adiciona novos emojis com novos métodos (como emojis em tons de pele criados por pré-amaldiçoar um personagem com um personagem adicional), etc.

Eu finalmente desisti e apenas escrevi o seguinte método que funciona para todos os emojis atuais e deve funcionar para todos os emojis futuros.

A solução cria um UILabel com o personagem e um fundo preto. CG então tira um instantâneo do rótulo e eu examino todos os pixels do instantâneo em busca de pixels não pretos sólidos. A razão pela qual adicionei o fundo preto é para evitar problemas de cores falsas devido à renderização de subpixel

A solução roda MUITO rápido no meu dispositivo, posso verificar centenas de caracteres por segundo, mas deve-se notar que esta é uma solução CoreGraphics e não deve ser usada pesadamente como você faria com um método de texto normal. O processamento de gráficos exige muitos dados, portanto, verificar milhares de caracteres de uma vez pode resultar em um atraso perceptível.

-(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
fonte
5
Gosto do seu pensamento! ;) - Sai da caixa!
Ramon de
Por que você está fazendo isso conosco? #apple #unicodestandard 😱🤔🤪🙈😈🤕💩
d4Rk
Faz um tempo que não vejo isso, mas me pergunto se preciso converter para UIColor e depois para hsb; parece que posso apenas verificar se r, g, b all == 0? Se alguém tentar, me avise
Albert Renshaw
eu gosto dessa solução, mas ela não vai quebrar com um personagem como ℹ?
Juan Carlos Ospina Gonzalez
1
@JuanCarlosOspinaGonzalez Não, em emoji que é renderizado como uma caixa azul com um i branco. Ele traz um bom ponto, porém, que o UILabel deve forçar a fonte a ser AppleColorEmoji, acrescentando que agora como um fail safe, embora eu ache que a Apple irá padronizá-la para essas de qualquer maneira
Albert Renshaw
2

Para o Swift 3.0.2, a seguinte resposta é a mais simples:

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
fonte
2

A resposta absolutamente semelhante àquelas que escrevi antes de mim, mas com um conjunto atualizado de escalares de 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
fonte
1

Existe uma boa solução para a tarefa mencionada. Mas verificar Unicode.Scalar.Properties de escalares Unicode é bom para um único caractere. E não é flexível o suficiente para Strings.

Podemos usar expressões regulares em vez - uma abordagem mais universal. Há uma descrição detalhada de como funciona abaixo. E aqui vai a solução.

A solução

No Swift, você pode verificar se uma String é um único caractere Emoji, usando uma extensão com tal propriedade computada:

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
    }

}

Como funciona (em detalhes)

Um único Emoji (um glifo) pode ser reproduzido por uma série de diferentes símbolos, sequências e suas combinações. Especificação Unicode define várias representações possíveis de caracteres Emoji.

Emoji de um único caractere

Um caractere Emoji reproduzido por um único Unicode Scalar.

O Unicode define o caractere emoji como:

emoji_character := \p{Emoji}

Mas isso não significa necessariamente que tal personagem será desenhado como um Emoji. Um símbolo numérico comum “1” tem a propriedade Emoji sendo verdadeira, embora ainda possa ser desenhado como texto. E há uma lista de tais símbolos: #, ©, 4, etc.

Deve-se pensar que podemos usar uma propriedade adicional para verificar: “Emoji_Presentation”. Mas não funciona assim. Existe um Emoji como 🏟 ou 🛍, que tem a propriedade Emoji_Presentation = false.

Para ter certeza de que o personagem é desenhado como Emoji por padrão, devemos verificar sua categoria: deve ser “Other_symbol”.

Portanto, na verdade, a expressão regular para Emoji de um único caractere deve ser definida como:

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

Sequência de apresentação de emoji

Um personagem, que normalmente pode ser desenhado como texto ou como Emoji. Sua aparência depende de um símbolo especial seguinte, um seletor de apresentação, que indica o tipo de apresentação. \ x {FE0E} define a representação de texto. \ x {FE0F} define a representação de emoji.

A lista de tais símbolos pode ser encontrada [aqui] (
 https://unicode.org/Public/emoji/12.1/emoji-variation-sequences.txt ).

O Unicode define a sequência de apresentação como esta:

emoji_presentation_sequence := emoji_character emoji_presentation_selector

Sequência de expressão regular para ele:

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

Emoji Keycap Sequence

A sequência se parece muito com a sequência de apresentação, mas tem escalar adicional no final: \ x {20E3}. O escopo dos escalares de base possíveis usados ​​para isso é bastante estreito: 0-9 # * - e isso é tudo. Exemplos: 1️⃣, 8️⃣, * ️⃣.

O Unicode define a sequência de teclas como esta:

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

Expressão regular para isso:

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

Sequência de modificador de emoji

Alguns Emojis podem ter uma aparência modificada, como um tom de pele. Por exemplo, Emoji 🧑 pode ser diferente: 🧑🧑🏻🧑🏼🧑🏽🧑🏾🧑🏿. Para definir um Emoji, que é chamado de “Emoji_Modifier_Base” neste caso, pode-se usar um subsequente “Emoji_Modifier”.

Em geral, essa sequência tem a seguinte aparência:

emoji_modifier_sequence := emoji_modifier_base emoji_modifier

Para detectá-lo, podemos pesquisar uma sequência de expressão regular:

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

Emoji Flag Sequence

As bandeiras são emojis com sua estrutura particular. Cada bandeira é representada por dois símbolos “Regional_Indicator”.

Unicode os define como:

emoji_flag_sequence := regional_indicator regional_indicator

Por exemplo, bandeira da Ucrânia 🇺🇦 de fato é representada por dois escalares: \ u {0001F1FA \ u {0001F1E6}

Expressão regular para isso:

emoji_flag_sequence := \p{RI}{2}

Sequência de Emoji Tag (ETS)

Uma sequência que usa uma chamada tag_base, que é seguida por uma especificação de tag personalizada composta de um intervalo de símbolos \ x {E0020} - \ x {E007E} e concluída por tag_end mark \ x {E007F}.

Unicode o define assim:

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}

O estranho é que o Unicode permite que a tag seja baseada em emoji_modifier_sequence ou emoji_presentation_sequence em ED-14a . Mas, ao mesmo tempo, em expressões regulares fornecidas na mesma documentação, eles parecem verificar a sequência com base em um único caractere Emoji.

Na lista de Emojis Unicode 12.1, existem apenas três desses Emojis definidos. Todas são bandeiras dos países do Reino Unido: Inglaterra 🏴󠁧󠁢󠁥󠁮󠁧󠁿, Escócia 🏴󠁧󠁢󠁳󠁣󠁴󠁿 e País de Gales 🏴󠁧󠁢󠁷󠁬󠁳󠁿. E todos eles são baseados em um único personagem Emoji. Portanto, é melhor verificarmos apenas essa sequência.

Expressão regular:

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

Emoji Zero-Width Joiner Sequence (sequência ZWJ)

Um joiner de largura zero é um escalar \ x {200D}. Com sua ajuda, vários personagens, que já são Emojis por si só, podem ser combinados em novos.

Por exemplo, uma “família com pai, filho e filha” Emoji 👨‍👧‍👦 é reproduzida por uma combinação de Emojis pai 👨, filha 👧 e filho 👦 colados com símbolos ZWJ.

É permitido colar elementos, que são caracteres Emoji simples, sequências de apresentação e modificadores.

A expressão regular para tal sequência em geral se parece com isto:

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

Expressão regular para todos eles

Todas as representações de Emoji mencionadas acima podem ser descritas por uma única expressão 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
fonte
-1

Tive o mesmo problema e acabei fazendo um Stringe Characterextensões.

O código é muito longo para postar, pois ele realmente lista todos os emojis (da lista unicode oficial v5.0) em um, CharacterSetvocê pode encontrá-lo aqui:

https://github.com/piterwilson/StringEmoji

Constantes

let emojiCharacterSet: CharacterSet

Conjunto de caracteres contendo todos os emojis conhecidos (conforme descrito na Lista Unicode 5.0 oficial http://unicode.org/emoji/charts-5.0/emoji-list.html )

Corda

var isEmoji: Bool {get}

Se a Stringinstância representa ou não um único caractere Emoji conhecido

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

Se a Stringinstância contém ou não um caractere Emoji conhecido

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

Aplica um kCFStringTransformToUnicodeName- CFStringTransformem uma cópia da 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}

Retorna o resultado de a kCFStringTransformToUnicodeName- CFStringTransformcom \N{prefixos e }sufixos removidos

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

Personagem

var isEmoji: Bool {get}

Se a Characterinstância representa ou não um caractere Emoji conhecido

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