String'deki Karakterin emoji olup olmadığını öğrenin.


92

Bir dizedeki bir karakterin emoji olup olmadığını öğrenmem gerekiyor.

Örneğin, şu karaktere sahibim:

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

Bu karakterin bir emoji olup olmadığını öğrenmem gerekiyor.


Merak ediyorum: neden bu bilgiye ihtiyacın var?
Martin R

@EricD .: Birden fazla UTF-8 kod noktası (ör. "€" = E2 82 AC) veya birden fazla UTF-16 kod noktası (ör. "𝄞" = D834 DD1E) alan birçok Unicode karakteri vardır.
Martin R


Dizelerin kendi indekslemeleri vardır, bu da onları kullanmanın tercih edilen bir yoludur. Belirli bir karakteri (veya bunun yerine grafem kümesini) elde etmek için şunları yapabilirsiniz: let character = string[string.index(after: string.startIndex)]veya let secondCharacter = string[string.index(string.startIndex, offsetBy: 1)]
Paul B

Yanıtlar:


239

Karşılaştığım şey, karakterler, unicode skalerleri ve glifler arasındaki farktır.

Örneğin, 👨‍👨‍👧‍👧 glifi 7 unicode skalerden oluşur:

Başka bir örnek, 👌🏿 glifi 2 unicode skalerden oluşur:

  • Normal emoji: 👌
  • Bir cilt tonu değiştirici: 🏿

Son olarak, glif 1️⃣ üç unicode karakter içerir:

Dolayısıyla karakterleri oluştururken ortaya çıkan glifler gerçekten önemlidir.

Swift 5.0 ve üzeri, bu işlemi çok daha kolaylaştırır ve yapmamız gereken bazı tahminlerden kurtulur. Unicode.Scalar'in yeni Propertyyardım türü, neyle uğraştığımızı belirlemektir. Ancak, bu özellikler yalnızca glif içindeki diğer skalarları kontrol ederken anlam ifade eder. Bu nedenle, bize yardımcı olmak için Character sınıfına bazı kolaylık yöntemleri ekleyeceğiz.

Daha fazla ayrıntı için, bunun nasıl çalıştığını açıklayan bir makale yazdım .

Swift 5.0 için, size şu sonucu verir:

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

Bu size aşağıdaki sonuçları verecektir:

"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

Daha eski Swift sürümleri için, eski kodumu içeren bu özete bakın.


6
Bu, buradaki açık ara en iyi ve en doğru cevaptır. Teşekkür ederim! Küçük bir not, örnekleriniz kodla eşleşmiyor (ön bilgide containsOnlyEmoki'yi containsEmoji olarak yeniden adlandırdınız - Sanırım daha doğru olduğu için, testlerimde karışık karakterlere sahip dizeler için true döndürdü).
Tim Bull

3
Benim hatam, bazı kodların etrafında değiştim, sanırım her şeyi batırdım. Örneği güncelledim
Kevin R.

2
@Andrew: Elbette, bunu göstermek için örneğe başka bir yöntem ekledim :).
Kevin R.

2
@Andrew, işin gerçekten dağınık olduğu yer. Bunun nasıl yapılacağına dair bir örnek ekledim. Sorun şu ki, CoreText'in sadece karakterleri kontrol ederek glifleri nasıl işleyeceğini bildiğimi varsayıyorum. Herhangi birinin daha temiz bir yöntem için önerisi varsa lütfen bana bildirin.
Kevin R

3
@Andrew Bunu belirttiğiniz için teşekkürler, containsOnlyEmojikontrolleri değiştirdim . Örneği Swift 3.0'a da güncelledim.
Kevin R

51

Bunu başarmanın en basit, en temiz ve en hızlı yolu, dizedeki her karakter için Unicode kod noktalarını bilinen emoji ve dingbats aralıklarına göre kontrol etmektir, örneğin:

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
Bunun gibi bir kod örneği, üçüncü taraf bir kitaplık bağımlılığı eklemeyi önermekten çok daha iyidir. Shardul'un cevabı akılsızca bir tavsiyedir - daima kendi kodunuzu yazın.
thefaj

Bu harika, davaların neyle ilgili olduğunu yorumladığınız için teşekkür ederiz
Shawn Throop

2
Kodunuz gibi, burada bir cevapta uyguladım . Fark ettiğim bir şey, bazı emojileri özlediği, belki de listelediğiniz kategorilerin parçası olmadığı için, örneğin bu: Robot Face emojisi 🤖
Cue

1
@Tel Sanırım aralık 0x1F900...0x1F9FF(Wikipedia başına) olacaktır. Tüm aralığın emoji olarak kabul edilmesi gerektiğinden emin değil.
Frizlab

11

Swift 5.0

… Tam olarak bunu kontrol etmenin yeni bir yolunu sundu!

Kendi Stringiçine girmelisin Scalars. Her Scalarbirinin Propertydeğeri destekleyen bir değeri vardır isEmoji!

Aslında, Skalar'ın bir Emoji değiştiricisi veya daha fazlası olup olmadığını bile kontrol edebilirsiniz. Apple'ın belgelerine bakın: https://developer.apple.com/documentation/swift/unicode/scalar/properties

Bunun isEmojiPresentationyerine kontrol etmeyi düşünebilirsiniz isEmoji, çünkü Apple aşağıdakileri belirtir isEmoji:

Bu özellik, varsayılan olarak emoji olarak oluşturulan skalarlar için ve ayrıca U + FE0F VARIATION SELECTOR-16 ile takip edildiğinde varsayılan olmayan bir emoji oluşturmaya sahip skalarlar için de geçerlidir. Bu, genellikle emoji olarak kabul edilmeyen bazı skalerleri içerir.


Bu yol aslında Emoji'leri tüm değiştiricilere ayırır, ancak kullanımı çok daha kolaydır. Ve Swift artık değiştiricili Emoji'leri saydığı için (örneğin: 👨‍👩‍👧‍👦, 👨🏻‍💻, 🏴) her tür şeyi yapabileceğiniz 1 olarak.

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, tüm Emoji'leri almanın ilginç bir yolunu işaret ediyor:

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
Harika cevap, teşekkürler. Swift 5'in bu bölümünü kullanmak için min sdk değerinizin 10.2 olması gerektiğini belirtmekte fayda var. Ayrıca bir dizenin yalnızca emojilerden oluşup oluşmadığını kontrol etmek için şu özelliklerden birine sahip olup olmadığını kontrol scalar.properties.isEmoji scalar.properties.isEmojiPresentation scalar.properties.isEmojiModifier scalar.properties.isEmojiModifierBase scalar.properties.isJoinControl scalar.properties.isVariationSelector
etmeliydim

6
Dikkat edin, 0-9 arası tam sayılar emoji olarak kabul edilir. Yani "6".unicodeScalars.first!.properties.isEmojişu şekilde değerlendirilecektrue
Miniroo

1
Gibi başka karakterler de var #ve *bu da isEmojiçek için true dönecek . isEmojiPresentationDöndürdüğü en azından daha iyi iş gibi görünüyor falseiçin 0...9, #, *ve diğer sembol Bir İngiliz-ABD klavyede deneyebilirsiniz. Herkesin onunla daha fazla deneyimi var ve girdi doğrulama için güvenilir olup olmadığını biliyor mu?
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
    }
}

Bu, güncellenmiş aralıklarla düzeltmem.


7

Swift 5 ile artık dizenizdeki her karakterin unicode özelliklerini inceleyebilirsiniz. Bu bize isEmojiher harf için uygun değişkeni verir . Sorun, isEmoji0-9 gibi 2 baytlık bir emojiye dönüştürülebilen herhangi bir karakter için doğru olarak dönecek.

Değişkene bakabilir isEmojive belirsiz karakterlerin bir emoji olarak görüntülenip görüntülenmeyeceğini belirlemek için bir emoji değiştiricinin varlığını kontrol edebiliriz.

Bu çözüm, burada sunulan normal ifade çözümlerinden çok daha ileriye dönük olmalıdır.

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

Bize ver

"hey".containsEmoji() //false

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

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

1
Ve dahası var olduğunu Character("3️⃣").isEmoji // trueikenCharacter("3").isEmoji // false
Paul B

4

Swift 3 Notu:

cnui_containsEmojiCharactersMetodun kaldırıldığı veya farklı bir dinamik kütüphaneye taşınmış olduğu görülüyor . _containsEmojiyine de çalışmalı.

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:

Kısa bir süre önce NSString, bir dizenin Emoji karakteri içerip içermediğini tespit etmek için işlevselliği açığa çıkaran özel bir API keşfettim :

let str: NSString = "hello😊"

Bir objc protokolü ile ve unsafeBitCast:

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

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

İle valueForKey:

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

Saf bir Swift dizesi ile, dizeyi AnyObjectkullanmadan önce olduğu gibi çevirmelisiniz valueForKey:

let str = "hello😊"

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

NSString başlık dosyasında bulunan yöntemler .


Aradığım şey bu, Teşekkürler JAL

Bu Apple tarafından reddedilecek mi?
Andrey Chernukha

@AndreyChernukha Her zaman bir risk vardır, ancak henüz herhangi bir reddedilme yaşamadım.
JAL

Asla özel API'leri kullanmayın. En iyi ihtimalle, acı sadece yarın gelir. Ya da gelecek ay.
xaphod

3

Bu kod örneğini veya bu bölmeyi kullanabilirsiniz .

Swift'de kullanmak için kategoriyi YourProject_Bridging_Header

#import "NSString+EMOEmoji.h"

Ardından String'inizdeki her emojinin aralığını kontrol edebilirsiniz:

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

let containsEmoji: Bool = example.emo_containsEmoji()

    print(containsEmoji)

// Output: ["true"]

Yukarıdaki kodla küçük bir örnek proje oluşturdum.


3

Geleceğin Kanıtı: Karakterin piksellerini manuel olarak kontrol edin; yeni emojiler eklendikçe diğer çözümler bozulur (ve bozulur).

Not: Bu Objective-C'dir (Swift'e dönüştürülebilir)

Yıllar geçtikçe, Apple yeni yöntemlerle yeni emojiler ekledikçe (ek bir karaktere sahip bir karaktere önceden küfrederek oluşturulan cilt tonlu emojiler gibi), bu emoji algılama çözümleri kırılmaya devam ediyor.

Sonunda ayrıldım ve mevcut tüm emojiler için çalışan ve gelecekteki tüm emojiler için çalışması gereken aşağıdaki yöntemi yazdım.

Çözüm, karakter ve siyah arka plana sahip bir UILabel oluşturur. CG daha sonra etiketin bir anlık görüntüsünü alıyor ve anlık görüntüdeki tüm pikselleri düz siyah olmayan pikseller için tarıyorum. Siyah arka planı eklememin nedeni, Alt Piksel Oluşturma nedeniyle yanlış renklendirme sorunlarından kaçınmaktır.

Çözüm cihazımda ÇOK hızlı çalışıyor, saniyede yüzlerce karakteri kontrol edebiliyorum, ancak bunun bir CoreGraphics çözümü olduğu ve normal bir metin yöntemiyle yaptığınız gibi yoğun bir şekilde kullanılmaması gerektiği unutulmamalıdır. Grafik işleme veri ağırdır, bu nedenle aynı anda binlerce karakteri kontrol etmek, fark edilir bir gecikmeye neden olabilir.

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

5
Düşünmeni beğendim! ;) - Kutudan dışarı!
Ramon

Bunu bize neden yapıyorsun? #apple #unicodestandard 😱🤔🤪🙈😈🤕💩
d4Rk

Buna bir süredir bakmadım ama UIColor'a hsb'ye çevirmem gerekip gerekmediğini merak ediyorum; Görünüşe göre r, g, b all == 0 olduğunu kontrol edebilirim? Birisi bana haber vermeye çalışırsa
Albert Renshaw

Bu çözümü beğendim, ancak ℹ gibi bir karakterle bozulmayacak mı?
Juan Carlos Ospina Gonzalez

1
@JuanCarlosOspinaGonzalez Hayır, beyaz i ile mavi bir kutu olarak görünen emojide. UILabel'in yazı tipini zorlaması gerektiği halde iyi bir noktayı ortaya çıkarıyor AppleColorEmoji, bunu şimdi bir başarısızlık kasası olarak ekliyor, ancak Apple'ın yine de bunu varsayılan olarak kullanacağını düşünüyorum
Albert Renshaw

2

Swift 3.0.2 için, aşağıdaki cevap en basit olanıdır:

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

Benden önce yazanlara kesinlikle benzer cevap, ancak güncellenmiş emoji skaler seti ile.

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

}


1

Bahsedilen görev için güzel bir çözüm var . Ancak Unicode.Scalar.Properties of Unicode skalerini kontrol etmek tek bir Karakter için iyidir. Ve Dizeler için yeterince esnek değil.

Bunun yerine Normal İfadeleri kullanabiliriz - daha evrensel bir yaklaşım. Aşağıda nasıl çalıştığına dair ayrıntılı bir açıklama var. İşte çözüm geliyor.

Çözüm

Swift'de bir String'in tek bir Emoji karakteri olup olmadığını, böyle bir hesaplanmış özelliğe sahip bir uzantı kullanarak kontrol edebilirsiniz:

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
    }

}

Nasıl çalışır (ayrıntılı olarak)

Tek bir Emoji (bir glif) bir dizi farklı sembol, sekans ve bunların kombinasyonlarıyla yeniden üretilebilir. Unicode özelliği, birkaç olası Emoji karakter temsilini tanımlar.

Tek Karakterli Emoji

Tek bir Unicode Skalar ile yeniden üretilen bir Emoji karakteri.

Unicode, Emoji Karakterini şu şekilde tanımlar:

emoji_character := \p{Emoji}

Ancak bu, böyle bir karakterin Emoji olarak çizileceği anlamına gelmez. Sıradan bir sayısal sembol "1", Emoji özelliğine sahiptir, ancak yine de metin olarak çizilebilir. Ve bu tür sembollerin bir listesi var: #, ©, 4, vb.

Kontrol etmek için ek özellik kullanabileceğimizi düşünmek gerekir: "Emoji_Presentation". Ama bu böyle çalışmıyor. Emoji_Presentation = false özelliğine sahip 🏟 veya 🛍 gibi bir Emoji vardır.

Karakterin varsayılan olarak Emoji olarak çizildiğinden emin olmak için, kategorisini kontrol etmeliyiz: "Diğer_sembol" olmalıdır.

Bu nedenle, aslında Tek Karakterli Emoji için normal ifade şu şekilde tanımlanmalıdır:

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

Emoji Sunum Dizisi

Normalde metin veya Emoji olarak çizilebilen bir karakter. Görünümü, sunum türünü belirten özel bir aşağıdaki sembole, bir sunum seçiciye bağlıdır. \ x {FE0E} metin gösterimini tanımlar. \ x {FE0F} emoji temsilini tanımlar.

Bu tür sembollerin listesi [burada] bulunabilir (
 https://unicode.org/Public/emoji/12.1/emoji-variation-sequences.txt ).

Unicode, sunum sırasını şu şekilde tanımlar:

emoji_presentation_sequence := emoji_character emoji_presentation_selector

Bunun için düzenli ifade dizisi:

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

Emoji Klavye Tuş Sırası

Dizi, Sunum dizisine çok benziyor, ancak sonunda ek skaler var: \ x {20E3}. Bunun için kullanılan olası temel skalerlerin kapsamı oldukça dardır: 0-9 # * - ve hepsi bu. Örnekler: 1️⃣, 8️⃣, * ️⃣.

Unicode, klavye tuş dizisini şu şekilde tanımlar:

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

Bunun için düzenli ifade:

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

Emoji Değiştirici Dizisi

Bazı Emojiler, cilt tonu gibi değiştirilmiş görünüme sahip olabilir. Örneğin Emoji 🧑 farklı olabilir: 🧑🧑🏻🧑🏼🧑🏽🧑🏾🧑🏿. Bu durumda "Emoji_Modifier_Base" olarak adlandırılan bir Emoji tanımlamak için, bir sonraki "Emoji_Modifier" kullanılabilir.

Genelde böyle bir sıra şuna benzer:

emoji_modifier_sequence := emoji_modifier_base emoji_modifier

Bunu tespit etmek için normal bir ifade dizisi arayabiliriz:

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

Emoji Bayrak Dizisi

Bayraklar, belirli yapılarıyla Emojilerdir. Her bayrak iki "Regional_Indicator" sembolü ile temsil edilir.

Unicode bunları şu şekilde tanımlar:

emoji_flag_sequence := regional_indicator regional_indicator

Örneğin, Ukrayna bayrağı 🇺🇦 aslında iki skaler ile temsil edilir: \ u {0001F1FA \ u {0001F1E6}

Bunun için düzenli ifade:

emoji_flag_sequence := \p{RI}{2}

Emoji Etiketi Sırası (ETS)

Sözde tag_base kullanan bir sekans, bunu \ x {E0020} - \ x {E007E} semboller dizisinden oluşan ve tag_end mark \ x {E007F} ile biten özel bir etiket spesifikasyonu izler.

Unicode bunu şu şekilde tanımlar:

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}

Garip olan şey, Unicode'un etiketin ED- 14a'daki emoji_modifier_sequence veya emoji_presentation_sequence öğesini temel almasına izin vermesidir . Ancak aynı dokümantasyonda sağlanan normal ifadelerde aynı zamanda sırayı yalnızca tek bir Emoji karakterine göre kontrol ediyor gibi görünüyorlar.

Unicode 12.1 Emoji listesinde, bu tür tanımlanmış yalnızca üç Emoji vardır . Hepsi Birleşik Krallık ülkelerinin bayraklarıdır: İngiltere 🏴󠁧󠁢󠁥󠁮󠁧󠁿, İskoçya 🏴󠁧󠁢󠁳󠁣󠁴󠁿 ve Galler 🏴󠁧󠁢󠁷󠁬󠁳󠁿. Ve hepsi tek bir Emoji karakterine dayanıyor. Öyleyse, sadece böyle bir sekansı kontrol etsek iyi olur.

Düzenli ifade:

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

Emoji Sıfır Genişlikli Birleştirme Dizisi (ZWJ dizisi)

Sıfır genişlikli bir birleştirici, skaler bir \ x {200D} 'dir. Onun yardımıyla zaten kendi başlarına Emojis olan birkaç karakter yenileriyle birleştirilebilir.

Örneğin, "baba, oğul ve kızdan oluşan bir aile" Emoji 👨‍👧‍ father, ZWJ sembolleriyle birbirine yapıştırılmış baba 👨, kız 👧 ve oğul Emojilerin bir kombinasyonu ile yeniden üretilir.

Tek Emoji karakterleri, Sunum ve Değiştirici dizileri olan öğelerin birbirine yapıştırılmasına izin verilir.

Bu tür bir dizi için düzenli ifade genel olarak şuna benzer:

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

Hepsi İçin Normal İfade

Yukarıda bahsedilen Emoji temsillerinin tümü tek bir düzenli ifade ile tanımlanabilir:

\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

Ben de aynı sorunu yaşadım ve bir Stringve Characteruzantıları oluşturmaya başladım.

Kod, tüm emojileri (resmi unicode listesi v5.0'dan) listelediği için gönderilemeyecek kadar uzun. CharacterSetBurada bulabilirsiniz:

https://github.com/piterwilson/StringEmoji

Sabitler

let emojiCharacterSet: CharacterSet

Bilinen tüm emojileri içeren karakter seti (resmi Unicode List 5.0 http://unicode.org/emoji/charts-5.0/emoji-list.html'de açıklandığı gibi )

Dize

var isEmoji: Bool {get}

StringÖrneğin bilinen tek bir Emoji karakterini temsil edip etmediği

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

Olsun ya da olmasın Stringörneği bilinen Emoji karakter içeriyor

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

Dizenin bir kopyasına bir kCFStringTransformToUnicodeName- CFStringTransformuygular

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

İade bir sonucudur kCFStringTransformToUnicodeName- CFStringTransformile \N{ön ek ve }son ekler kaldırıldı

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

Karakter

var isEmoji: Bool {get}

CharacterÖrneğin bilinen bir Emoji karakterini temsil edip etmediği

print("".isEmoji) // false
print("😁".isEmoji) // true
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.