ตรวจสอบว่าตัวละครใน String เป็นอีโมจิหรือไม่?


92

ฉันต้องการค้นหาว่าอักขระในสตริงเป็นอีโมจิหรือไม่

ตัวอย่างเช่นฉันมีตัวละครนี้:

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

ฉันต้องการค้นหาว่าตัวละครนั้นเป็นอีโมจิหรือไม่


ฉันอยากรู้: ทำไมคุณถึงต้องการข้อมูลนั้น?
Martin R

@EricD: มีอักขระ Unicode จำนวนมากที่ใช้จุดรหัส UTF-8 มากกว่าหนึ่งจุด (เช่น "€" = E2 82 AC) หรือจุดรหัส UTF-16 มากกว่าหนึ่งจุด (เช่น "𝄞" = D834 DD1E)
Martin R

หวังว่าคุณจะได้แนวคิดจากโค้ด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

คำตอบ:


239

สิ่งที่ฉันสะดุดคือความแตกต่างระหว่างอักขระ Unicode สเกลาร์และร่ายมนตร์

ตัวอย่างเช่นสัญลักษณ์ 👨‍👨‍👧‍👧 ประกอบด้วย 7 unicode สเกลาร์:

อีกตัวอย่างหนึ่งสัญลักษณ์👌🏿ประกอบด้วยสเกลาร์ยูนิโคด 2 ตัว:

  • อิโมจิปกติ: 👌
  • ตัวปรับสีผิว: 🏿

อันสุดท้ายสัญลักษณ์1️⃣ประกอบด้วยอักขระ Unicode สามตัว:

ดังนั้นเมื่อแสดงผลตัวละครร่ายมนตร์ที่ได้จึงมีความสำคัญมาก

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 ในตัวอย่างข้อมูล - ฉันคิดว่าเป็นเพราะถูกต้องมากกว่าในการทดสอบของฉันส่งคืนจริงสำหรับสตริงที่มีอักขระผสมกัน)
Tim Bull

3
ฉันไม่ดีฉันเปลี่ยนรหัสบางอย่างเดาว่าฉันทำผิด ฉันอัปเดตตัวอย่าง
Kevin R

2
@ แอนดรูว์: แน่นอนว่าฉันได้เพิ่มวิธีการอื่นในตัวอย่างเพื่อสาธิตสิ่งนี้ :)
Kevin R

2
@ แอนดรูว์นี่มันยุ่งจริงๆ ฉันได้เพิ่มตัวอย่างวิธีการทำ ปัญหาคือฉันสมมติว่ารู้ว่า CoreText จะแสดงร่ายมนตร์อย่างไรโดยเพียงแค่ตรวจสอบตัวละคร หากใครมีคำแนะนำสำหรับวิธีที่สะอาดกว่าโปรดแจ้งให้เราทราบ
Kevin R

3
@ แอนดรูขอบคุณที่ชี้ให้เห็นว่าฉันเปลี่ยนวิธีการcontainsOnlyEmojiตรวจสอบ ฉันยังอัปเดตตัวอย่างเป็น Swift 3.0
Kevin R

51

วิธีที่ง่ายที่สุดสะอาดที่สุดและรวดเร็วที่สุดในการทำสิ่งนี้คือการตรวจสอบจุดรหัส Unicode สำหรับอักขระแต่ละตัวในสตริงเทียบกับช่วงอีโมจิและ dingbats ที่รู้จักเช่น:

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
ตัวอย่างโค้ดเช่นนี้เป็นวิธีที่ดีกว่าการแนะนำให้รวมการพึ่งพาไลบรารีของบุคคลที่สาม คำตอบของ Shardul คือคำแนะนำที่ไม่ฉลาดให้ปฏิบัติตาม - เขียนโค้ดของคุณเองเสมอ
thefaj

ยอดเยี่ยมมากขอบคุณสำหรับความคิดเห็นเกี่ยวกับกรณีที่เกี่ยวข้อง
Shawn Throop

2
ชอบมากรหัสของคุณผมดำเนินการในคำตอบที่นี่ สิ่งที่ฉันสังเกตเห็นคือมันพลาดอิโมจิบางตัวอาจเป็นเพราะมันไม่ได้เป็นส่วนหนึ่งของหมวดหมู่ที่คุณระบุไว้ตัวอย่างเช่น Robot Face emoji 🤖
คิว

1
@ เทลฉันเดาว่ามันน่าจะเป็นช่วง0x1F900...0x1F9FF(ต่อวิกิพีเดีย) ไม่แน่ใจว่าทุกช่วงควรถือเป็นอีโมจิ
Frizlab

11

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 นับอีโมจิที่มีตัวดัดแปลง (เช่น: 👨‍👩‍👧‍👦, 👨🏻‍💻, as) เป็น 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
สปริงแฮม

6
ระวังจำนวนเต็ม 0-9 ถือเป็นอิโมจิ ดังนั้น"6".unicodeScalars.first!.properties.isEmojiจะประเมินเป็นtrue
Miniroo

1
มีอักขระอื่น ๆ เช่น#และ*จะคืนค่าจริงสำหรับการisEmojiตรวจสอบ isEmojiPresentationดูเหมือนว่าจะทำงานได้ดีขึ้นอย่างน้อยก็จะส่งกลับfalseสำหรับ0...9, #, *และสัญลักษณ์อื่น ๆ ที่ฉันอาจจะลองบนแป้นพิมพ์ภาษาอังกฤษสหรัฐอเมริกา ใครมีประสบการณ์มากกว่านี้และรู้ว่าสามารถเชื่อถือได้สำหรับการตรวจสอบอินพุตหรือไม่?
ม.ค.

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

นี่คือการแก้ไขของฉันพร้อมช่วงที่อัปเดต


7

ด้วย Swift 5 คุณสามารถตรวจสอบคุณสมบัติ Unicode ของแต่ละอักขระในสตริงของคุณได้แล้ว สิ่งนี้ทำให้เรามีisEmojiตัวแปรที่สะดวกสำหรับแต่ละตัวอักษร ปัญหาisEmojiจะกลับมาเป็นจริงสำหรับอักขระใด ๆ ที่สามารถแปลงเป็นอีโมจิขนาด 2 ไบต์เช่น 0-9

เราสามารถดูตัวแปรisEmojiและตรวจสอบการมีอยู่ของตัวปรับแต่งอิโมจิเพื่อตรวจสอบว่าอักขระที่ไม่ชัดเจนจะแสดงเป็นอีโมจิหรือไม่

โซลูชันนี้น่าจะเป็นข้อพิสูจน์ในอนาคตมากกว่าโซลูชัน regex ที่นำเสนอที่นี่

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ที่แสดงฟังก์ชันการตรวจจับว่าสตริงมีอักขระอิโมจิหรือไม่:

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 หรือไม่?
Andrey Chernukha

@AndreyChernukha มีความเสี่ยงอยู่เสมอ แต่ฉันยังไม่พบการปฏิเสธใด ๆ
JAL

ไม่เคยใช้ API ส่วนตัว อย่างดีที่สุดความเจ็บปวดจะมาถึงในวันพรุ่งนี้เท่านั้น หรือเดือนหน้า.
xaphod

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 จะจับภาพฉลากและฉันสแกนพิกเซลทั้งหมดในภาพรวมเพื่อหาพิกเซลที่ไม่ใช่สีดำทึบ เหตุผลที่ฉันเพิ่มพื้นหลังสีดำคือเพื่อหลีกเลี่ยงปัญหาการระบายสีที่ผิดพลาดเนื่องจากเดอร์ Subpixel

โซลูชันทำงานเร็วมากบนอุปกรณ์ของฉันฉันสามารถตรวจสอบอักขระได้หลายร้อยตัวต่อวินาที แต่ควรสังเกตว่านี่เป็นโซลูชัน 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;
    
}

5
ฉันชอบความคิดของคุณ! ;) - ออกจากกล่อง!
รามอน

ทำไมคุณถึงทำกับเราแบบนี้ #apple #unicodestandard 😱🤔🤪🙈😈🤕💩
d4Rk

ฉันไม่ได้ดูสิ่งนี้มาระยะหนึ่งแล้ว แต่ฉันสงสัยว่าฉันต้องแปลงเป็น UIColor แล้วเป็น hsb; ดูเหมือนว่าฉันสามารถตรวจสอบได้ว่า r, g, b ทั้งหมด == 0? หากมีใครลองแจ้งให้เราทราบ
Albert Renshaw

ฉันชอบวิธีแก้ปัญหานี้ แต่จะไม่ทำลายตัวละครอย่าง like หรือไม่?
Juan Carlos Ospina Gonzalez

1
@JuanCarlosOspinaGonzalez Nope ในอิโมจิที่แสดงเป็นกล่องสีน้ำเงินพร้อมตัว i สีขาว มันเป็นจุดที่ดีแม้ว่า UILabel ควรบังคับให้แบบอักษรเป็นAppleColorEmojiและเพิ่มว่าในขณะนี้เป็นความล้มเหลวที่ปลอดภัยแม้ว่าฉันคิดว่า Apple จะเริ่มต้นสำหรับสิ่งเหล่านั้นก็ตาม
Albert Renshaw

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

}


1

มีทางออกที่ดีสำหรับงานดังกล่าว แต่การตรวจสอบ Unicode.Scalar คุณสมบัติของ Unicode scalars นั้นดีสำหรับตัวละครตัวเดียว และไม่ยืดหยุ่นเพียงพอสำหรับ Strings

เราสามารถใช้ Regular Expressions แทนได้ - แนวทางที่เป็นสากลมากขึ้น มีรายละเอียดวิธีการทำงานด้านล่าง และนี่คือวิธีแก้ปัญหา

การแก้ไขปัญหา

ใน 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 ที่เป็นไปได้หลายแบบ

อีโมจิแบบอักขระเดี่ยว

อักขระอิโมจิที่สร้างขึ้นโดย Unicode Scalar ตัวเดียว

Unicode กำหนด Emoji Character เป็น:

emoji_character := \p{Emoji}

แต่ก็ไม่ได้แปลว่าจะวาดตัวละครดังกล่าวเป็นอีโมจิ สัญลักษณ์ตัวเลขธรรมดา“ 1” มีคุณสมบัติ Emoji เป็นจริงแม้ว่าจะยังคงวาดเป็นข้อความได้ และมีรายการสัญลักษณ์ดังกล่าว: #, ©, 4 ฯลฯ

เราควรคิดว่าเราสามารถใช้คุณสมบัติเพิ่มเติมเพื่อตรวจสอบ:“ Emoji_Presentation” แต่มันไม่ได้ผลเช่นนี้ มี Emoji เช่น🏟หรือ🛍ซึ่งมีคุณสมบัติ Emoji_Presentation = false

เพื่อให้แน่ใจว่าอักขระนั้นถูกวาดเป็นอีโมจิตามค่าเริ่มต้นเราควรตรวจสอบหมวดหมู่ของมันโดยควรเป็น“ Other_symbol”

ดังนั้นในความเป็นจริงนิพจน์ทั่วไปสำหรับ Emoji แบบอักขระเดี่ยวควรถูกกำหนดเป็น:

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

ลำดับการนำเสนอ Emoji

อักขระซึ่งโดยปกติสามารถวาดเป็นข้อความหรือเป็นอิโมจิได้ ลักษณะจะขึ้นอยู่กับสัญลักษณ์พิเศษต่อไปนี้ตัวเลือกการนำเสนอซึ่งระบุประเภทการนำเสนอ \ 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

ลำดับดูเหมือนกับลำดับการนำเสนอมาก แต่มีสเกลาร์เพิ่มเติมในตอนท้าย: \ 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}

ลำดับแท็กอิโมจิ (ETS)

ลำดับที่ใช้สิ่งที่เรียกว่า tag_base ซึ่งตามด้วยข้อกำหนดแท็กที่กำหนดเองซึ่งประกอบด้วยช่วงของสัญลักษณ์ \ x {E0020} - \ x {E007E} และสรุปโดย tag_end mark \ 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 แต่ในเวลาเดียวกันในนิพจน์ทั่วไปที่ให้ไว้ในเอกสารเดียวกันพวกเขาดูเหมือนจะตรวจสอบลำดับตามอักขระอิโมจิตัวเดียวเท่านั้น

ในรายการ Unicode 12.1 Emoji มีEmojisเพียงสามตัวเท่านั้นกำหนดไว้ทั้งหมดเป็นธงของประเทศในสหราชอาณาจักร: อังกฤษ🏴󠁧󠁢󠁥󠁮󠁧󠁿สกอตแลนด์🏴󠁧󠁢󠁳󠁣󠁴󠁿และเวลส์🏴󠁧󠁢󠁷󠁬󠁳󠁿 และทั้งหมดนี้ขึ้นอยู่กับอักขระ Emoji ตัวเดียว ดังนั้นเราควรตรวจสอบลำดับดังกล่าวเท่านั้นดีกว่า

นิพจน์ทั่วไป:

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

Emoji Zero-Width Joiner Sequence (ลำดับ ZWJ)

ตัวเชื่อมความกว้างเป็นศูนย์คือสเกลาร์ \ x {200D} ด้วยความช่วยเหลือของตัวละครหลายตัวซึ่งเป็น Emojis ด้วยตัวเองสามารถรวมกันเป็นตัวใหม่ได้

ตัวอย่างเช่น "ครอบครัวที่มีพ่อลูกชายและลูกสาว" อีโมจิ 👨‍👧‍👦 ถูกจำลองขึ้นโดยการรวมกันของพ่อ👨ลูกสาว👧และลูกชาย👦อีโมจิที่ติดด้วยสัญลักษณ์ ZWJ

อนุญาตให้รวมองค์ประกอบเข้าด้วยกันซึ่ง ได้แก่ อักขระ Emoji เดี่ยวลำดับการนำเสนอและตัวปรับแต่ง

นิพจน์ทั่วไปสำหรับลำดับดังกล่าวโดยทั่วไปมีลักษณะดังนี้:

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ส่วนขยาย

รหัสยาวเกินไปที่จะโพสต์เนื่องจากแสดงรายการอิโมจิทั้งหมด (จากรายการยูนิโคดอย่างเป็นทางการ v5.0) ในรายการที่CharacterSetคุณสามารถค้นหาได้ที่นี่:

https://github.com/piterwilson/StringEmoji

ค่าคงที่

ให้ emojiCharacterSet: CharacterSet

ชุดอักขระที่มีอิโมจิที่รู้จักทั้งหมด (ตามที่อธิบายไว้ในรายการ Unicode อย่างเป็นทางการ 5.0 http://unicode.org/emoji/charts-5.0/emoji-list.html )

สตริง

var isEmoji: Bool {get}

Stringอินสแตนซ์นั้นแสดงถึงอักขระอิโมจิเดี่ยวที่รู้จักหรือไม่

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

Stringอินสแตนซ์นั้นมีอักขระอิโมจิที่รู้จักหรือไม่

print("".containsEmoji) // false
print("😁".containsEmoji) // true
print("😁😜".containsEmoji) // true
var unicodeName: สตริง {get}

ใช้ a kCFStringTransformToUnicodeName- CFStringTransformบนสำเนาของ String

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อินสแตนซ์แสดงถึงอักขระอิโมจิที่รู้จักหรือไม่

print("".isEmoji) // false
print("😁".isEmoji) // true
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.