Узнать, является ли символ в строке эмодзи?


90

Мне нужно выяснить, является ли символ в строке смайликом.

Например, у меня есть такой персонаж:

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

Мне нужно выяснить, является ли этот персонаж смайликом.


Мне любопытно: зачем вам эта информация?
Martin R

@EricD .: Существует много символов Unicode, которые принимают более одной кодовой точки UTF-8 (например, «€» = E2 82 AC) или более одной кодовой точки UTF-16 (например, «𝄞» = D834 DD1E).
Martin R

Надеюсь, вы получите представление об этой версии кода obj-c stackoverflow.com/questions/19886642/…
Ашиш Каккад

Строки имеют свою индексацию, что является предпочтительным способом их использования. Чтобы получить конкретный символ (или, скорее, кластер графем), вы могли бы: let character = string[string.index(after: string.startIndex)]или let secondCharacter = string[string.index(string.startIndex, offsetBy: 1)]
Paul B

Ответы:


229

Я наткнулся на разницу между символами, скалярами Unicode и глифами.

Например, глиф 👨‍👨‍👧‍👧 состоит из 7 скаляров юникода:

Другой пример, глиф 👌🏿 состоит из двух скаляров юникода:

  • Обычный смайлик: 👌
  • Модификатор тона кожи: 🏿

Последний, глиф 1️⃣, содержит три символа Юникода:

Поэтому при рендеринге символов действительно имеют значение полученные глифы.

Swift 5.0 и выше значительно упрощает этот процесс и избавляет от некоторых догадок, которые нам нужно было делать. Unicode.ScalarНовый Propertyтип помогает определить, с чем мы имеем дело. Однако эти свойства имеют смысл только при проверке других скаляров внутри глифа. Вот почему мы добавим несколько удобных методов в класс Character, чтобы помочь нам.

Для более подробной информации я написал статью, объясняющую, как это работает .

Для Swift 5.0 это оставляет вам следующий результат:

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

Это даст вам следующие результаты:

"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

Для более старых версий Swift ознакомьтесь с этой сутью, содержащей мой старый код.


6
Это, безусловно, лучший и самый правильный ответ. Спасибо! Одно небольшое примечание: ваши примеры не соответствуют коду (вы переименовали containsOnlyEmoki в containsEmoji во фрагменте - я полагаю, потому что это более правильно, в моем тестировании он вернул true для строк со смешанными символами).
Тим Булл,

3
Моя проблема, я изменил код, думаю, я напортачил. Я обновил пример
Kevin R

2
@Andrew: Конечно, я добавил в пример еще один метод, чтобы продемонстрировать это :).
Кевин Р.

2
@ Эндрю, вот где все становится по-настоящему беспорядочно. Я добавил пример, как это сделать. Проблема в том, что я должен знать, как CoreText будет отображать глифы, просто проверяя символы. Если у кого-то есть предложения по более чистому методу, дайте мне знать.
Кевин Р.

3
@Andrew Спасибо, что указали на это, я изменил способ containsOnlyEmojiпроверки. Я также обновил пример до Swift 3.0.
Кевин Р.

48

Самый простой, чистый и быстрый способ добиться этого - просто проверить кодовые точки Unicode для каждого символа в строке на известные диапазоны эмодзи и дингбатов, например:

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
Такой пример кода лучше, чем предлагать включить зависимость сторонней библиотеки. Ответ Шардула - неразумный совет - всегда пишите свой собственный код.
thefaj

Это здорово, спасибо, что прокомментировали, к чему относятся эти дела,
Шон Труп

1
Как и ваш код, я реализовал его здесь . Я заметил, что в нем отсутствуют некоторые смайлы, возможно, потому, что они не входят в перечисленные вами категории, например, этот: смайлики с изображением лица робота 🤖
Cue

1
@Tel Я думаю, это будет диапазон 0x1F900...0x1F9FF(согласно Википедии). Не уверен, что смайликами следует считать весь диапазон.
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
    }
}

Это мое исправление с обновленными диапазонами.


8

Swift 5.0

… Представил новый способ проверки именно этого!

Вы должны сломать ваш Stringв его Scalars. У каждого Scalarесть Propertyзначение, которое поддерживает это isEmojiзначение!

На самом деле вы даже можете проверить, является ли Scalar модификатором Emoji или более. Ознакомьтесь с документацией Apple: https://developer.apple.com/documentation/swift/unicode/scalar/properties.

Вы можете рассмотреть возможность проверки isEmojiPresentationвместо isEmoji, потому что Apple заявляет следующее для isEmoji:

Это свойство верно для скаляров, которые по умолчанию визуализируются как эмодзи, а также для скаляров, которые имеют нестандартный рендеринг эмодзи, за которым следует U + FE0F VARIATION SELECTOR-16. Сюда входят некоторые скаляры, которые обычно не считаются смайликами.


Таким образом, Emoji разбивается на все модификаторы, но с ним намного проще справиться. И поскольку Swift теперь считает эмодзи с модификаторами (например: 👨‍👩‍👧‍👦, 👨🏻‍💻, 🏴) как 1, вы можете делать все, что угодно.

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 указывает на интересный способ получить все 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
Отличный ответ, спасибо. Стоит отметить, что ваш min sdk должен быть 10,2, чтобы использовать эту часть Swift 5. Также, чтобы проверить, состоит ли строка только из смайликов, мне пришлось проверить, имеет ли она одно из следующих свойств:scalar.properties.isEmoji scalar.properties.isEmojiPresentation scalar.properties.isEmojiModifier scalar.properties.isEmojiModifierBase scalar.properties.isJoinControl scalar.properties.isVariationSelector
Springham

6
Остерегайтесь, целые числа 0-9 считаются смайликами. Итак "6".unicodeScalars.first!.properties.isEmoji, true
оценим

6

В Swift 5 теперь вы можете проверять свойства юникода каждого символа в вашей строке. Это дает нам удобную isEmojiпеременную для каждой буквы. Проблема в том, isEmojiчто вернет true для любого символа, который может быть преобразован в 2-байтовый смайлик, например 0-9.

Мы можем посмотреть на переменную, isEmojiа также проверить наличие модификатора эмодзи, чтобы определить, будут ли неоднозначные символы отображаться как эмодзи.

Это решение должно быть гораздо более перспективным, чем предлагаемые здесь решения с регулярными выражениями.

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

Давая нам

"hey".containsEmoji() //false

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

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

1
И более того, Character("3️⃣").isEmoji // trueпокаCharacter("3").isEmoji // false
Paul B

4

Swift 3 Примечание:

Похоже, cnui_containsEmojiCharactersметод был удален или перемещен в другую динамическую библиотеку. _containsEmojiвсе равно должно работать.

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:

Недавно я обнаружил частный API, в NSStringкотором доступны функции для определения наличия в строке символа Emoji:

let str: NSString = "hello😊"

С протоколом objc и unsafeBitCast:

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

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

С valueForKey:

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

С чистой строкой Swift вы должны преобразовать строку, как AnyObjectперед использованием valueForKey:

let str = "hello😊"

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

Методы, найденные в файле заголовка NSString .


Это то, что я ищу, спасибо JAL

Будет ли это отклонено Apple?
Андрей Чернуха

@AndreyChernukha Риск есть всегда, но отторжения пока не было.
JAL

Никогда не используйте частные API. В лучшем случае боль наступит завтра. Или в следующем месяце.
xaphod 02

3

Вы можете использовать этот пример кода или этот модуль .

Чтобы использовать его в Swift, импортируйте категорию в YourProject_Bridging_Header

#import "NSString+EMOEmoji.h"

Затем вы можете проверить диапазон для каждого смайлика в своей строке:

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

let containsEmoji: Bool = example.emo_containsEmoji()

    print(containsEmoji)

// Output: ["true"]

Я создал небольшой пример проекта с приведенным выше кодом.


3

Перспективы: вручную проверьте пиксели персонажа; другие решения сломаются (и сломаются) по мере добавления новых смайлов.

Примечание: это Objective-C (может быть преобразован в Swift)

На протяжении многих лет эти решения для обнаружения смайликов продолжают ломаться, поскольку Apple добавляет новые смайлы с новыми методами (например, смайлики с оттенком кожи, созданные путем предварительного проклятия персонажа дополнительным персонажем) и т. Д.

В конце концов я сломался и просто написал следующий метод, который работает для всех текущих смайлов и должен работать для всех будущих смайлов.

Решение создает UILabel с символом и черным фоном. Затем CG делает снимок этикетки, и я просматриваю все пиксели снимка на предмет любых не сплошных черных пикселей. Причина, по которой я добавляю черный фон, - избежать проблем с ложной окраской из-за субпиксельного рендеринга.

Решение работает ОЧЕНЬ быстро на моем устройстве, я могу проверять сотни символов в секунду, но следует отметить, что это решение CoreGraphics и не должно интенсивно использоваться, как вы могли бы с обычным текстовым методом. Обработка графики требует больших объемов данных, поэтому одновременная проверка тысяч символов может привести к заметным задержкам.

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

4
Мне нравится ваше мышление! ;) - Из коробки!
Ramon

Почему ты это с нами делаешь? #apple #unicodestandard 😱🤔🤪🙈😈🤕💩
d4Rk

Я давно не смотрел на это, но мне интересно, нужно ли мне преобразовывать в UIColor, а затем в hsb; кажется, я могу просто проверить, что r, g, b all == 0? Если кто-то попытается, дайте мне знать
Альберт Реншоу

Мне нравится это решение, но не сломается ли он с таким символом, как ℹ?
Хуан Карлос Оспина Гонсалес

1
@JuanCarlosOspinaGonzalez Нет, в эмодзи, который отображается как синий прямоугольник с белым i. Тем не менее, это вызывает хороший момент, что UILabel должен принудительно использовать шрифт AppleColorEmoji, добавив это в качестве отказоустойчивого, хотя я думаю, что Apple в любом случае будет использовать его по умолчанию для них
Альберт Реншоу

2

Для Swift 3.0.2 самый простой ответ:

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

Абсолютно аналогичный ответ на те, что писали до меня, но с обновленным набором скаляров эмодзи.

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


extension UnicodeScalar {

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

}


0

Для указанной задачи есть хорошее решение . Но проверка Unicode.Scalar.Properties скаляров Unicode хороша для одного символа. И недостаточно гибок для струнных.

Вместо этого мы можем использовать регулярные выражения - более универсальный подход. Ниже приводится подробное описание того, как это работает. И вот решение.

Решение

В Swift вы можете проверить, является ли String одним символом Emoji, используя расширение с таким вычисляемым свойством:

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
    }

}

Как это работает (подробнее)

Один эмодзи (глиф) может быть воспроизведен с помощью ряда различных символов, последовательностей и их комбинаций. Спецификация Unicode определяет несколько возможных представлений символов Emoji.

Односимвольные эмодзи

Символ Emoji, воспроизводимый одним скаляром Unicode.

Unicode определяет символ Emoji как:

emoji_character := \p{Emoji}

Но это не обязательно означает, что такой персонаж будет нарисован как эмодзи. Обычный числовой символ «1» имеет свойство Emoji, равное истине, хотя он все равно может отображаться как текст. И есть список таких символов: #, ©, 4 и т. Д.

Надо думать, что для проверки можно использовать дополнительное свойство: «Emoji_Presentation». Но так не работает. Есть эмодзи, например 🏟 или 🛍, у которых свойство Emoji_Presentation = false.

Чтобы убедиться, что персонаж по умолчанию отрисовывается как Emoji, мы должны проверить его категорию: это должно быть «Other_symbol».

Итак, на самом деле регулярное выражение для односимвольных эмодзи следует определять как:

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

Последовательность презентации эмодзи

Персонаж, который обычно можно рисовать как текст или как эмодзи. Его внешний вид зависит от специального следующего символа, селектора представления, который указывает его тип представления. \ x {FE0E} определяет текстовое представление. \ x {FE0F} определяет представление эмодзи.

Список таких символов можно найти [здесь] (
 https://unicode.org/Public/emoji/12.1/emoji-variation-sequences.txt ).

Unicode определяет последовательность представления следующим образом:

emoji_presentation_sequence := emoji_character emoji_presentation_selector

Последовательность регулярных выражений для него:

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

Emoji Keycap Sequence

Последовательность очень похожа на последовательность представления, но в конце есть дополнительный скаляр: \ x {20E3}. Набор возможных базовых скаляров, используемых для этого, довольно узок: 0-9 # * - и все. Примеры: 1️⃣, 8️⃣, * ️⃣.

Unicode определяет последовательность клавиш следующим образом:

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

Регулярное выражение для него:

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

Последовательность модификатора эмодзи

Некоторые эмодзи могут иметь измененный вид, например оттенок кожи. Например Emoji 🧑 может быть другим: 🧑🧑🏻🧑🏼🧑🏽🧑🏾🧑🏿. Чтобы определить Emoji, который в данном случае называется «Emoji_Modifier_Base», можно использовать последующий «Emoji_Modifier».

В целом такая последовательность выглядит так:

emoji_modifier_sequence := emoji_modifier_base emoji_modifier

Чтобы обнаружить это, мы можем искать последовательность регулярных выражений:

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

Последовательность флагов эмодзи

Флаги - это эмодзи с их особой структурой. Каждый флаг представлен двумя символами «Regional_Indicator».

Юникод определяет их как:

emoji_flag_sequence := regional_indicator regional_indicator

Например флаг Украины 🇺🇦 по сути представлен двумя скалярами: \ u {0001F1FA \ u {0001F1E6}

Регулярное выражение для него:

emoji_flag_sequence := \p{RI}{2}

Последовательность тегов эмодзи (ETS)

Последовательность, которая использует так называемую tag_base, за которой следует спецификация настраиваемого тега, состоящая из диапазона символов \ x {E0020} - \ x {E007E} и завершенная меткой tag_end \ x {E007F}.

Юникод определяет это так:

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}

Странно то, что Unicode позволяет тегу быть основанным на emoji_modifier_sequence или emoji_presentation_sequence в ED-14a . Но в то же время в регулярных выражениях, представленных в той же документации, они, похоже, проверяют последовательность только на основе одного символа Emoji.

В списке эмодзи Unicode 12.1 определены только три таких эмодзи . Все они являются флагами стран Великобритании: Англии 🏴󠁧󠁢󠁥󠁮󠁧󠁿, Шотландии 🏴󠁧󠁢󠁳󠁣󠁴󠁿 и Уэльса 🏴󠁧󠁢󠁷󠁬󠁳󠁿. И все они основаны на одном персонаже Emoji. Так что лучше проверять только такую ​​последовательность.

Регулярное выражение:

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

Последовательность стыковки нулевой ширины Emoji (последовательность ZWJ)

Соединитель нулевой ширины - это скаляр \ x {200D}. С его помощью несколько персонажей, которые сами по себе уже являются Emojis, могут быть объединены в новых.

Например, смайлик «семья с отцом, сыном и дочерью» 👨‍👧‍👦 воспроизводится комбинацией смайликов отца 👨, дочери 👧 и сына, склеенных с символами ZWJ.

Разрешено склеивать элементы, которыми являются одиночные символы эмодзи, последовательности презентаций и модификаторов.

Регулярное выражение для такой последовательности в целом выглядит так:

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

Регулярное выражение для всех

Все упомянутые выше представления эмодзи можно описать одним регулярным выражением:

\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

я была такая же проблема и в конечном итоге делает Stringи Characterрасширения.

Код слишком длинный для публикации, так как на самом деле в нем перечислены все смайлы (из официального списка Unicode v5.0), CharacterSetвы можете найти его здесь:

https://github.com/piterwilson/StringEmoji

Константы

let emojiCharacterSet: CharacterSet

Набор символов, содержащий все известные смайлы (как описано в официальном Unicode List 5.0 http://unicode.org/emoji/charts-5.0/emoji-list.html )

Строка

var isEmoji: Bool {get}

Независимо от того, Stringпредставляет ли экземпляр известного одиночного символа Emoji

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

StringСодержит ли экземпляр известного персонажа Emoji

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

Применяет kCFStringTransformToUnicodeName- CFStringTransformк копии строки

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

Возвращает результат kCFStringTransformToUnicodeName- CFStringTransformс удаленными \N{префиксами и }суффиксами

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

символ

var isEmoji: Bool {get}

Независимо от того, Characterпредставляет ли экземпляр известного персонажа Emoji

print("".isEmoji) // false
print("😁".isEmoji) // true
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.