Дізнайтеся, чи є символ у рядку смайликом?


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/…
Ashish Kakkad

Рядки мають своє індексування, що є кращим способом їх використання. Щоб отримати певний персонаж (або скоріше let character = string[string.index(after: string.startIndex)]let secondCharacter = string[string.index(string.startIndex, offsetBy: 1)]
Paul B

Відповіді:


229

Я натрапив на різницю між символами, сканерами Unicode та гліфами.

Наприклад, гліф 👨‍👨‍👧‍👧 складається з 7 сканерів Unicode:

Інший приклад, гліф 👌🏿 складається з 2 сканарів Unicode:

  • Звичайні смайли: 👌
  • Модифікатор відтінку шкіри: 🏿

Останній, гліф 1️⃣, містить три символи Unicode:

Отже, при отрисуванні символів отримані гліфи дійсно мають значення.

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

Більш детально я написав статтю, в якій пояснював, як це працює .

Для 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
Моя погана, я змінив якийсь код, мабуть, я переплутав. Я оновив приклад
Кевін Р

2
@Andrew: Звичайно, я додав до прикладу ще один метод, щоб продемонструвати це :).
Kevin R

2
@Andrew тут стає дуже брудно. Я додав приклад, як це зробити. Проблема полягає в тому, що я припускаю знати, як CoreText буде відображати гліфи, просто перевіряючи символи. Якщо хтось має пропозиції щодо більш чистого методу, будь ласка, повідомте мене.
Kevin R

3
@Andrew Дякую, що вказав на це, я змінив спосіб containsOnlyEmojiперевірки. Я також оновив приклад до Swift 3.0.
Kevin R

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
Як і ваш код, я реалізував його у відповіді тут . Що я помітив, так це те, що він пропускає деякі смайли, можливо тому, що вони не є частиною перелічених вами категорій, наприклад ця: Робоче обличчя смайликів C
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

Свіфт 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 на всі модифікатори, але це набагато простіше в обробці. І оскільки Свіфт зараз зараховує Emoji з модифікаторами (наприклад: 👨‍👩‍👧‍👦, 👨🏻‍💻, 🏴) як 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 вказує на цікавий спосіб отримати всі емодзі:

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
Miniroo

6

За допомогою Swift 5 ви тепер можете перевіряти властивості unicode кожного символу у вашому рядку. Це дає нам зручну isEmojiзмінну для кожної літери. Проблема полягає у isEmojiповерненні істини для будь-якого символу, який можна перетворити на 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

Свіфт 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

Свіфт 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. У кращому випадку шкода настане лише завтра. Або наступного місяця.
ксафод

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
Мені подобається ваше мислення! ;) - З коробки!
Рамон,

Чому ти робиш це з нами? #apple #unicodestandard 😱🤔🤪🙈😈🤕💩
d4Rk

Я давно не дивився на це, але мені цікаво, чи потрібно мені перетворювати на UIColor, а потім на hsb; здається, я можу просто перевірити, що r, g, b все == 0? Якщо хтось спробує, дайте мені знати
Альберт Реншо

мені подобається це рішення, але хіба воно не порветься з таким символом, як ℹ?
Хуан Карлос Оспіна Гонсалес,

1
@JuanCarlosOspinaGonzalez Nope, у смайликах, що виглядає як синя коробка з білим 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 юнікодських скалярів хороша для одного персонажа. І недостатньо гнучкий для струн.

Натомість ми можемо використовувати регулярні вирази - більш універсальний підхід. Нижче наведено детальний опис того, як це працює. І тут іде рішення.

Рішення

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

}

Як це працює (детально)

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

Емодзі з одним символом

Персонаж Emoji, відтворений одним скаляром Unicode.

Unicode визначає символ Emoji як:

emoji_character := \p{Emoji}

Але це не обов'язково означає, що такий персонаж буде намальований як смайлик. Звичайний числовий символ "1" має властивість Emoji бути істинним, хоча воно все ще може бути намальоване як текст. І є перелік таких символів: #, ©, 4 тощо.

Слід подумати, що ми можемо використовувати додаткове властивість для перевірки: “Emoji_Presentation”. Але це не працює так. Є такі смайли, як 🏟 або 🛍, які мають властивість Emoji_Presentation = false.

Щоб переконатися, що символ за замовчуванням намальований як Emoji, ми повинні перевірити його категорію: це має бути «Інший_символ».

Отже, насправді регулярний вираз для односимвольних смайлів слід визначати як:

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

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

Символ, який зазвичай можна намалювати як текст або як Emoji. Зовнішній вигляд залежить від спеціального наступного символу, селектора презентації, який вказує тип його презентації. \ x {FE0E} визначає подання тексту. \ x {FE0F} визначає подання смайликів.

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

Юнікод визначає послідовність презентацій таким чином:

emoji_presentation_sequence := emoji_character emoji_presentation_selector

Послідовність регулярних виразів для нього:

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

Послідовність клавіатури Emoji

Послідовність дуже схожа на послідовність презентації, але вона має додатковий скаляр в кінці: \ 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}

Послідовність прапора Emoji

Прапори - це емодзі з їх особливою структурою. Кожен прапор представлений двома символами "Regional_Indicator".

Unicode визначає їх як:

emoji_flag_sequence := regional_indicator regional_indicator

Наприклад, прапор України 🇺🇦 насправді представлений двома скалярами: \ u {0001F1FA \ u {0001F1E6}

Регулярний вираз для нього:

emoji_flag_sequence := \p{RI}{2}

Послідовність тегів Emoji (ETS)

Послідовність, яка використовує так звану базу_тегів, за якою слідує спеціальна специфікація тегу, складена з діапазону символів \ x {E0020} - \ x {E007E} і завершена позначкою tag_end \ x {E007F}.

Unicode визначає це так:

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.

У списку Emoji Unicode 12.1 існує лише три таких Emoji . Всі вони є прапорами країн Великобританії: Англії 🏴󠁧󠁢󠁥󠁮󠁧󠁿, Шотландії 🏴󠁧󠁢󠁳󠁣󠁴󠁿 та Уельсу 🏴󠁧󠁢󠁷󠁬󠁳󠁿. І всі вони засновані на одному персонажі Emoji. Отже, нам краще перевірити лише таку послідовність.

Регулярний вираз:

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

Послідовність столярних робіт із нульовою шириною Emoji (послідовність ZWJ)

Столяр нульової ширини - це скаляр \ x {200D}. За його допомогою кілька персонажів, які вже самі по собі є емоджами, можна об’єднати в нових.

Наприклад, "сім'я з батьком, сином та дочкою" Emoji 👨‍👧‍👦 відтворюється поєднанням батька 👨, дочки 👧 та сина 👦 Emojis, склеєних символами ZWJ.

Дозволено склеювати елементи, які є одиничними символами Emoji, послідовностями презентації та модифікатора.

Регулярний вираз для такої послідовності загалом виглядає так:

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

Регулярний вираз для всіх них

Всі згадані вище подання Emoji можна описати одним регулярним виразом:

\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

у мене була та ж проблема, і я закінчив робити a Stringта Characterрозширення.

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

https://github.com/piterwilson/StringEmoji

Константи

let emojiCharacterSet: CharacterSet

Набір символів, що містить усі відомі смайли (як описано в офіційному списку Unicode 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 міститьEmoji: Bool {get}

Чи Stringмістить екземпляр відомий символ Emoji чи ні

print("".containsEmoji) // false
print("😁".containsEmoji) // true
print("😁😜".containsEmoji) // true
var unicodeName: Рядок {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: Рядок {get}

Повертає результат a 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.