Cari tahu apakah Karakter dalam String adalah emoji?

90

Saya perlu mencari tahu apakah karakter dalam string adalah emoji.

Misalnya, saya memiliki karakter ini:

let string = "๐Ÿ˜€"
let character = Array(string)[0]

Saya perlu mencari tahu apakah karakter itu adalah emoji.

Andrew
sumber
Saya penasaran: mengapa Anda membutuhkan informasi itu?
Martin R
@EricD .: Ada banyak karakter Unicode yang mengambil lebih dari satu titik kode UTF-8 (misalnya "โ‚ฌ" = E2 82 AC) atau lebih dari satu titik kode UTF-16 (misalnya "๐„ž" = D834 DD1E).
Martin R
Semoga Anda mendapat ide dari versi
object
String memiliki pengindeksannya sendiri yang merupakan cara yang disukai untuk menggunakannya. Untuk mendapatkan karakter tertentu (atau lebih tepatnya cluster grafik) Anda dapat: let character = string[string.index(after: string.startIndex)]atau let secondCharacter = string[string.index(string.startIndex, offsetBy: 1)]
Paul B

Jawaban:

229

Apa yang saya temukan adalah perbedaan antara karakter, skalar unicode, dan mesin terbang.

Misalnya, mesin terbang ๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง terdiri dari 7 skalar unicode:

  • Empat karakter emoji: ๐Ÿ‘จ๐Ÿ‘ฉ๐Ÿ‘ง๐Ÿ‘ง
  • Di antara setiap emoji ada karakter khusus, yang bekerja seperti lem karakter; lihat spesifikasi untuk info lebih lanjut

Contoh lain, mesin terbang ๐Ÿ‘Œ๐Ÿฟ terdiri dari 2 skalar unicode:

  • Emoji biasa: ๐Ÿ‘Œ
  • Pengubah warna kulit: ๐Ÿฟ

Yang terakhir, mesin terbang 1๏ธโƒฃ berisi tiga karakter unicode:

Jadi saat merender karakter, mesin terbang yang dihasilkan sangat penting.

Swift 5.0 dan yang lebih baru membuat proses ini lebih mudah dan menghilangkan beberapa tebakan yang perlu kami lakukan. Unicode.ScalarIni baru Propertyjenis membantu adalah menentukan apa yang kita berurusan sedang bersama. Namun, properti tersebut hanya masuk akal saat memeriksa skalar lain di dalam mesin terbang. Inilah mengapa kami akan menambahkan beberapa metode praktis ke kelas Karakter untuk membantu kami.

Untuk lebih detail, saya menulis artikel yang menjelaskan cara kerjanya .

Untuk Swift 5.0, hasilnya adalah sebagai berikut:

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

Yang akan memberi Anda hasil sebagai berikut:

"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

Untuk versi Swift yang lebih lama, lihat inti yang berisi kode lama saya.

Kevin R
sumber
6
Sejauh ini, ini adalah jawaban terbaik dan paling benar di sini. Terima kasih! Satu catatan kecil, contoh Anda tidak cocok dengan kode (Anda mengganti nama containsOnlyEmoki menjadi containsEmoji di cuplikan - saya kira karena itu lebih benar, dalam pengujian saya itu mengembalikan true untuk string dengan karakter campuran).
Tim Bull
3
Saya buruk, saya mengubah beberapa kode, kira saya mengacaukan. Saya memperbarui contoh
Kevin R
2
@ Andrew: Tentu, saya menambahkan metode lain ke contoh untuk mendemonstrasikan ini :).
Kevin R
2
@Andrew ini adalah tempat yang menjadi sangat berantakan. Saya menambahkan contoh bagaimana melakukan itu. Masalahnya adalah saya berasumsi untuk mengetahui bagaimana CoreText akan membuat mesin terbang hanya dengan memeriksa karakternya. Jika ada yang punya saran untuk metode yang lebih bersih, beri tahu saya.
Kevin R
3
@Andrew Terima kasih telah menunjukkan hal itu, saya mengubah cara containsOnlyEmojipemeriksaan. Saya juga memperbarui contoh ke Swift 3.0.
Kevin R
48

Cara termudah, paling bersih, dan tercepat untuk melakukannya adalah dengan memeriksa poin kode Unicode untuk setiap karakter dalam string terhadap rentang emoji dan dingbat yang diketahui, seperti:

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
sumber
9
Contoh kode seperti ini jauh lebih baik daripada menyarankan untuk menyertakan dependensi library pihak ketiga. Jawaban Shardul adalah nasihat yang tidak bijaksana untuk diikuti โ€” selalu tulis kode Anda sendiri.
thefaj
Ini bagus, terima kasih telah berkomentar tentang apa kasus tersebut
Shawn Throop
1
Seperti banyak kode Anda, saya menerapkannya dalam jawaban di sini . Satu hal yang saya perhatikan adalah ada beberapa emoji yang terlewat, mungkin karena itu bukan bagian dari kategori yang Anda daftarkan, misalnya yang ini: Emoji Wajah Robot ๐Ÿค–
Isyarat
1
@Tel Saya kira itu akan menjadi kisaran 0x1F900...0x1F9FF(per Wikipedia). Tidak yakin semua rentang harus dianggap emoji.
Frizlab
8
extension String {
    func containsEmoji() -> Bool {
        for scalar in unicodeScalars {
            switch scalar.value {
            case 0x3030, 0x00AE, 0x00A9,// Special Characters
            0x1D000...0x1F77F,          // Emoticons
            0x2100...0x27BF,            // Misc symbols and Dingbats
            0xFE00...0xFE0F,            // Variation Selectors
            0x1F900...0x1F9FF:          // Supplemental Symbols and Pictographs
                return true
            default:
                continue
            }
        }
        return false
    }
}

Ini adalah perbaikan saya, dengan rentang yang diperbarui.

Sebastian Lopez
sumber
8

Swift 5.0.0

โ€ฆ Memperkenalkan cara baru untuk memeriksa ini!

Anda harus membobol Anda Stringmenjadi miliknya Scalars. Masing-masing Scalarmemiliki Propertynilai yang mendukung isEmojinilai tersebut!

Sebenarnya Anda bahkan dapat memeriksa apakah Scalar adalah pengubah Emoji atau lebih. Lihat dokumentasi Apple: https://developer.apple.com/documentation/swift/unicode/scalar/properties

Anda mungkin ingin mempertimbangkan untuk memeriksa isEmojiPresentationdaripada isEmoji, karena Apple menyatakan berikut ini untuk isEmoji:

Properti ini berlaku untuk skalar yang dirender sebagai emoji secara default dan juga untuk skalar yang memiliki rendering emoji non-default jika diikuti oleh U + FE0F VARIATION SELECTOR-16. Ini termasuk beberapa skalar yang biasanya tidak dianggap sebagai emoji.


Cara ini sebenarnya membagi Emoji menjadi semua pengubah, tetapi cara ini lebih mudah untuk ditangani. Dan karena Swift sekarang menghitung Emoji dengan pengubah (misalnya: ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ, ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป, ๐Ÿด) sebagai 1, Anda dapat melakukan semua jenis hal.

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 menunjukkan cara menarik untuk mendapatkan semua Emoji:

import Foundation

var emoji = CharacterSet()

for codePoint in 0x0000...0x1F0000 {
    guard let scalarValue = Unicode.Scalar(codePoint) else {
        continue
    }

    // Implemented in Swift 5 (SE-0221)
    // https://github.com/apple/swift-evolution/blob/master/proposals/0221-character-properties.md
    if scalarValue.properties.isEmoji {
        emoji.insert(scalarValue)
    }
}
alexkaessner
sumber
1
Jawaban yang bagus, terima kasih. Perlu disebutkan bahwa min sdk Anda harus 10.2 untuk menggunakan bagian Swift 5. Juga untuk memeriksa apakah sebuah string hanya terdiri dari emoji, saya harus memeriksa apakah string itu memiliki salah satu properti berikut:scalar.properties.isEmoji scalar.properties.isEmojiPresentation scalar.properties.isEmojiModifier scalar.properties.isEmojiModifierBase scalar.properties.isJoinControl scalar.properties.isVariationSelector
A Springham
6
Hati-hati, bilangan bulat 0-9 dianggap emoji. Jadi "6".unicodeScalars.first!.properties.isEmojiakan dievaluasi sebagaitrue
Miniroo
6

Dengan Swift 5, Anda sekarang dapat memeriksa properti unicode dari setiap karakter dalam string Anda. Ini memberi kita isEmojivariabel yang nyaman di setiap huruf. Masalahnya adalah isEmojiakan mengembalikan nilai true untuk karakter apa pun yang dapat diubah menjadi emoji 2-byte, seperti 0-9.

Kita dapat melihat variabel isEmojidan juga memeriksa keberadaan pengubah emoji untuk menentukan apakah karakter ambigu akan ditampilkan sebagai emoji.

Solusi ini harus menjadi bukti masa depan yang jauh lebih banyak daripada solusi regex yang ditawarkan di sini.

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

Memberi kami

"hey".containsEmoji() //false

"Hello World ๐Ÿ˜Ž".containsEmoji() //true
"Hello World ๐Ÿ˜Ž".containsOnlyEmojis() //false

"3".containsEmoji() //false
"3๏ธโƒฃ".containsEmoji() //true
Miniroo
sumber
1
Dan terlebih lagi Character("3๏ธโƒฃ").isEmoji // truesementaraCharacter("3").isEmoji // false
Paul B
4

Swift 3 Catatan:

Tampaknya cnui_containsEmojiCharactersmetode tersebut telah dihapus atau dipindahkan ke pustaka dinamis yang berbeda. _containsEmojiharus tetap berfungsi.

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:

Saya baru-baru ini menemukan API pribadi NSStringyang memperlihatkan fungsionalitas untuk mendeteksi jika sebuah string berisi karakter Emoji:

let str: NSString = "hello๐Ÿ˜Š"

Dengan protokol objc dan unsafeBitCast:

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

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

Dengan valueForKey:

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

Dengan string Swift murni, Anda harus mentransmisikan string seperti AnyObjectsebelumnya menggunakan valueForKey:

let str = "hello๐Ÿ˜Š"

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

Metode yang ditemukan di file header NSString .

JAL
sumber
Inilah yang saya cari, Terima kasih JAL
Apakah ini akan ditolak oleh Apple?
Andrey Chernukha
@AndreyChernukha Selalu ada risiko, tapi saya belum pernah mengalami penolakan.
JAL
Jangan pernah menggunakan API pribadi. Paling banter, rasa sakit itu hanya akan datang besok. Atau bulan depan.
xaphod
3

Anda dapat menggunakan contoh kode ini atau pod ini .

Untuk menggunakannya di Swift, impor kategori ke dalam YourProject_Bridging_Header

#import "NSString+EMOEmoji.h"

Kemudian Anda dapat memeriksa kisaran untuk setiap emoji di String Anda:

let example: NSString = "string๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘งwith๐Ÿ˜emojisโœŠ๐Ÿฟ" //string with emojis

let containsEmoji: Bool = example.emo_containsEmoji()

    print(containsEmoji)

// Output: ["true"]

Saya membuat proyek contoh kecil dengan kode di atas.

Gabriel.Massana
sumber
3

Bukti Masa Depan: Secara manual memeriksa piksel karakter; solusi lain akan rusak (dan rusak) saat emoji baru ditambahkan.

Catatan: Ini adalah Objective-C (dapat diubah menjadi Swift)

Selama bertahun-tahun, solusi pendeteksi emoji ini terus rusak saat Apple menambahkan emoji baru dengan metode baru (seperti emoji dengan warna kulit yang dibuat dengan mengutuk karakter dengan karakter tambahan), dll.

Saya akhirnya rusak dan baru saja menulis metode berikut yang berfungsi untuk semua emoji saat ini dan harus berfungsi untuk semua emoji di masa mendatang.

Solusinya menciptakan UILabel dengan karakter dan latar belakang hitam. CG kemudian mengambil snapshot dari label dan saya memindai semua piksel dalam snapshot untuk piksel non-solid-black. Alasan saya menambahkan latar belakang hitam adalah untuk menghindari masalah pewarnaan palsu karena Subpixel Rendering

Solusinya berjalan SANGAT cepat di perangkat saya, saya dapat memeriksa ratusan karakter per detik, tetapi perlu dicatat bahwa ini adalah solusi CoreGraphics dan tidak boleh digunakan secara berlebihan seperti yang Anda bisa dengan metode teks biasa. Pemrosesan grafik adalah data yang berat sehingga memeriksa ribuan karakter sekaligus dapat menyebabkan kelambatan yang nyata.

-(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
sumber
4
Saya suka pemikiran Anda! ;) - Keluar dari kotak!
Ramon
Mengapa Anda melakukan ini pada kami? #apel #unicodestandard ๐Ÿ˜ฑ๐Ÿค”๐Ÿคช๐Ÿ™ˆ๐Ÿ˜ˆ๐Ÿค•๐Ÿ’ฉ
d4Rk
Saya sudah lama tidak melihat ini tetapi saya bertanya-tanya apakah saya harus mengonversi ke UIColor lalu ke hsb; sepertinya saya bisa memeriksa bahwa r, g, b all == 0? Jika seseorang mencoba, beri tahu saya
Albert Renshaw
Saya suka solusi ini, tetapi bukankah akan rusak dengan karakter seperti โ„น?
Juan Carlos Ospina Gonzalez
1
@JuanCarlosOspinaGonzalez Tidak, dalam emoji yang ditampilkan sebagai kotak biru dengan i putih. Itu memang memunculkan poin yang baik bahwa UILabel harus memaksa font menjadi AppleColorEmoji, menambahkan bahwa sekarang sebagai brankas yang gagal, meskipun saya pikir Apple akan default untuk itu
Albert Renshaw
2

Untuk Swift 3.0.2, jawaban berikut adalah yang paling sederhana:

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

Jawaban yang benar-benar mirip dengan yang saya tulis sebelumnya, tetapi dengan set skalar emoji yang diperbarui.

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
sumber
0

Ada solusi yang bagus untuk tugas yang disebutkan. Tapi Memeriksa Properti Unicode.Scalar.Properti skalar unicode bagus untuk satu Karakter. Dan tidak cukup fleksibel untuk Strings.

Sebagai gantinya, kita dapat menggunakan Ekspresi Reguler - pendekatan yang lebih universal. Ada penjelasan rinci tentang cara kerjanya di bawah ini. Dan inilah solusinya.

Solusinya

Di Swift Anda dapat memeriksa, apakah String adalah satu karakter Emoji, menggunakan ekstensi dengan properti yang dihitung:

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
    }

}

Bagaimana cara kerjanya (secara rinci)

Emoji tunggal (mesin terbang) dapat direproduksi dengan sejumlah simbol, urutan, dan kombinasinya yang berbeda. Spesifikasi Unicode mendefinisikan beberapa kemungkinan representasi karakter Emoji.

Emoji Karakter Tunggal

Karakter Emoji direproduksi oleh satu Unicode Scalar.

Unicode mendefinisikan Karakter Emoji sebagai:

emoji_character := \p{Emoji}

Tetapi itu tidak berarti bahwa karakter seperti itu akan digambar sebagai Emoji. Simbol numerik biasa "1" memiliki properti Emoji yang benar, meskipun masih dapat digambar sebagai teks. Dan ada daftar simbol seperti itu: #, ยฉ, 4, dll.

Orang harus berpikir, bahwa kita dapat menggunakan properti tambahan untuk memeriksa: "Emoji_Presentation". Tapi tidak berhasil seperti ini. Ada Emoji seperti ๐ŸŸ atau ๐Ÿ›, yang memiliki properti Emoji_Presentation = false.

Untuk memastikan, bahwa karakter digambar sebagai Emoji secara default, kita harus memeriksa kategorinya: ini harus "Simbol_lain".

Jadi, sebenarnya ekspresi reguler untuk Emoji Karakter Tunggal harus didefinisikan sebagai:

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

Urutan Presentasi Emoji

Karakter, yang biasanya dapat digambar sebagai teks atau Emoji. Penampilannya tergantung pada simbol khusus berikut, pemilih presentasi, yang menunjukkan jenis presentasinya. \ x {FE0E} mendefinisikan representasi teks. \ x {FE0F} mendefinisikan representasi emoji.

Daftar simbol tersebut dapat ditemukan [di sini] (โ€จ https://unicode.org/Public/emoji/12.1/emoji-variation-sequences.txt ).

Unicode mendefinisikan urutan presentasi seperti ini:

emoji_presentation_sequence := emoji_character emoji_presentation_selector

Urutan ekspresi reguler untuk itu:

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

Urutan Emoji Keycap

Urutannya terlihat sangat mirip dengan urutan Presentasi, tetapi memiliki skalar tambahan di akhir: \ x {20E3}. Cakupan skalar dasar yang mungkin digunakan untuk itu agak sempit: 0-9 # * - dan itu saja. Contoh: 1๏ธโƒฃ, 8๏ธโƒฃ, * ๏ธโƒฃ.

Unicode mendefinisikan urutan keycap seperti ini:

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

Ekspresi reguler untuk itu:

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

Urutan Pengubah Emoji

Beberapa Emoji dapat mengubah tampilan seperti warna kulit. Misalnya Emoji ๐Ÿง‘ bisa berbeda: ๐Ÿง‘๐Ÿง‘๐Ÿป๐Ÿง‘๐Ÿผ๐Ÿง‘๐Ÿฝ๐Ÿง‘๐Ÿพ๐Ÿง‘๐Ÿฟ. Untuk mendefinisikan Emoji, yang dalam hal ini disebut "Emoji_Modifier_Base", seseorang dapat menggunakan "Emoji_Modifier" berikutnya.

Secara umum urutan seperti ini terlihat seperti ini:

emoji_modifier_sequence := emoji_modifier_base emoji_modifier

Untuk mendeteksinya kita bisa mencari regular expression sequence:

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

Urutan Bendera Emoji

Bendera adalah Emoji dengan strukturnya yang khas. Setiap bendera diwakili dengan dua simbol "Regional_Indicator".

Unicode mendefinisikannya seperti:

emoji_flag_sequence := regional_indicator regional_indicator

Misalnya bendera Ukraina ๐Ÿ‡บ๐Ÿ‡ฆ sebenarnya diwakili dengan dua skalar: \ u {0001F1FA \ u {0001F1E6}

Ekspresi reguler untuk itu:

emoji_flag_sequence := \p{RI}{2}

Emoji Tag Sequence (ETS)

Urutan yang menggunakan apa yang disebut tag_base, yang diikuti dengan spesifikasi tag kustom yang terdiri dari berbagai simbol \ x {E0020} - \ x {E007E} dan diakhiri dengan tag_end mark \ x {E007F}.

Unicode mendefinisikannya seperti ini:

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}

Anehnya, Unicode memungkinkan tag didasarkan pada emoji_modifier_sequence atau emoji_presentation_sequence di ED-14a . Tetapi pada saat yang sama dalam ekspresi reguler yang disediakan pada dokumentasi yang sama, ekspresi reguler tampaknya memeriksa urutan berdasarkan satu karakter Emoji saja.

Dalam daftar Unicode 12.1 Emoji hanya ada tiga Emoji yang ditentukan. Semuanya adalah bendera negara Inggris: Inggris ๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ, Skotlandia ๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ dan Wales ๐Ÿด๓ ง๓ ข๓ ท๓ ฌ๓ ณ๓ ฟ. Dan semuanya didasarkan pada satu karakter Emoji. Jadi, sebaiknya kita memeriksa urutan seperti itu saja.

Ekspresi reguler:

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

Urutan Penyambung Lebar-Nol Emoji (urutan ZWJ)

Penggabung dengan lebar nol adalah skalar \ x {200D}. Dengan bantuannya, beberapa karakter, yang sudah menjadi Emoji sendiri, dapat digabungkan menjadi yang baru.

Misalnya, โ€œkeluarga dengan ayah, putra dan putriโ€ Emoji ๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ direproduksi dengan kombinasi ayah ๐Ÿ‘จ, anak perempuan ๐Ÿ‘ง dan anak laki-laki ๐Ÿ‘ฆ Emoji direkatkan dengan simbol ZWJ.

Diperbolehkan untuk menyatukan elemen, yaitu karakter Emoji Tunggal, urutan Presentasi dan Pengubah.

Ekspresi reguler untuk urutan tersebut secara umum terlihat seperti ini:

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

Ekspresi Reguler Untuk Semuanya

Semua representasi Emoji yang disebutkan di atas dapat dijelaskan dengan satu ekspresi reguler:

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

saya memiliki masalah yang sama dan akhirnya membuat Stringdan Characterekstensi.

Kode terlalu panjang untuk dikirim karena sebenarnya mencantumkan semua emoji (dari daftar unicode resmi v5.0) di a CharacterSetAnda dapat menemukannya di sini:

https://github.com/piterwilson/StringEmoji

Konstanta

biarkan emojiCharacterSet: CharacterSet

Kumpulan karakter yang berisi semua emoji yang diketahui (seperti yang dijelaskan dalam Unicode List 5.0 resmi http://unicode.org/emoji/charts-5.0/emoji-list.html )

Tali

var isEmoji: Bool {get}

Apakah Stringinstance mewakili satu karakter Emoji yang diketahui atau tidak

print("".isEmoji) // false
print("๐Ÿ˜".isEmoji) // true
print("๐Ÿ˜๐Ÿ˜œ".isEmoji) // false (String is not a single Emoji)
var berisiEmoji: Bool {get}

Apakah Stringinstance berisi karakter Emoji yang diketahui atau tidak

print("".containsEmoji) // false
print("๐Ÿ˜".containsEmoji) // true
print("๐Ÿ˜๐Ÿ˜œ".containsEmoji) // true
var unicodeName: String {get}

Menerapkan kCFStringTransformToUnicodeName- CFStringTransformpada salinan 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}

Mengembalikan hasil dari a kCFStringTransformToUnicodeName- CFStringTransformdengan \N{prefiks dan }sufiks dihapus

print("รก".unicodeName) // LATIN SMALL LETTER A WITH ACUTE
print("๐Ÿ˜œ".unicodeName) // FACE WITH STUCK-OUT TONGUE AND WINKING EYE

Karakter

var isEmoji: Bool {get}

Apakah Characterinstance mewakili karakter Emoji yang diketahui atau tidak

print("".isEmoji) // false
print("๐Ÿ˜".isEmoji) // true
Juan Carlos Ospina Gonzalez
sumber