Tìm hiểu xem Ký tự trong Chuỗi có phải là biểu tượng cảm xúc không?


90

Tôi cần tìm xem một ký tự trong chuỗi có phải là biểu tượng cảm xúc hay không.

Ví dụ, tôi có ký tự này:

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

Tôi cần tìm xem ký tự đó có phải là biểu tượng cảm xúc hay không.


Tôi tò mò: tại sao bạn cần thông tin đó?
Martin R

@EricD: Có nhiều ký tự Unicode sử dụng nhiều hơn một điểm mã UTF-8 (ví dụ: "€" = E2 82 AC) hoặc nhiều hơn một điểm mã UTF-16 (ví dụ: "𝄞" = D834 DD1E).
Martin R

Hy vọng bạn sẽ có một ý tưởng từ này phiên bản obj-c mã stackoverflow.com/questions/19886642/...
Ashish Kakkad

Các chuỗi có lập chỉ mục của chúng, đây là một cách được ưu tiên sử dụng. Để có được một ký tự cụ thể (hoặc cụm grapheme đúng hơn), bạn có thể: let character = string[string.index(after: string.startIndex)]hoặc let secondCharacter = string[string.index(string.startIndex, offsetBy: 1)]
Paul B

Câu trả lời:


229

Điều tôi vấp phải là sự khác biệt giữa các ký tự, vô hướng unicode và glyph.

Ví dụ, glyph 👨‍👨‍👧‍👧 bao gồm 7 vô hướng unicode:

Một ví dụ khác, glyph 👌🏿 bao gồm 2 vô hướng unicode:

  • Biểu tượng cảm xúc thông thường: 👌
  • Một công cụ sửa đổi tông màu da: 🏿

Cuối cùng, glyph 1️⃣ chứa ba ký tự unicode:

Vì vậy, khi kết xuất các ký tự, glyphs kết quả thực sự quan trọng.

Swift 5.0 trở lên làm cho quá trình này dễ dàng hơn nhiều và loại bỏ một số phỏng đoán mà chúng tôi cần phải làm. Loại Unicode.Scalarmới của Propertygiúp xác định những gì chúng tôi đang giải quyết. Tuy nhiên, những thuộc tính đó chỉ có ý nghĩa khi kiểm tra các vô hướng khác trong glyph. Đây là lý do tại sao chúng tôi sẽ thêm một số phương thức tiện lợi vào lớp Character để giúp chúng tôi.

Để biết thêm chi tiết, tôi đã viết một bài báo giải thích cách hoạt động của điều này .

Đối với Swift 5.0, nó để lại cho bạn kết quả sau:

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

Điều này sẽ cho bạn kết quả sau:

"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

Đối với các phiên bản Swift cũ hơn, hãy xem ý chính này chứa mã cũ của tôi.


6
Đây là câu trả lời tốt nhất và chính xác nhất ở đây. Cảm ơn bạn! Một lưu ý nhỏ, các ví dụ của bạn không khớp với mã (bạn đã đổi tên containsOnlyEmoki thành containsEmoji trong đoạn mã - Tôi đoán vì nó đúng hơn, trong thử nghiệm của tôi, nó trả về true cho các chuỗi có các ký tự hỗn hợp).
Tim Bull

3
Thật tệ, tôi đã thay đổi một số mã, đoán là tôi đã làm sai. Tôi đã cập nhật ví dụ
Kevin R

2
@Andrew: Chắc chắn rồi, tôi đã thêm một phương thức khác vào ví dụ để chứng minh điều này :).
Kevin R

2
@Andrew đây là nơi nó thực sự lộn xộn. Tôi đã thêm một ví dụ về cách làm điều đó. Vấn đề là tôi đã giả sử biết cách CoreText sẽ hiển thị các glyph bằng cách chỉ cần kiểm tra các ký tự. Nếu bất cứ ai có đề xuất cho một phương pháp sạch hơn, xin vui lòng cho tôi biết.
Kevin R

3
@Andrew Cảm ơn bạn đã chỉ ra điều đó, tôi đã thay đổi cách containsOnlyEmojikiểm tra. Tôi cũng đã cập nhật ví dụ này lên Swift 3.0.
Kevin R

48

Cách đơn giản nhất, sạch nhất, và swiftiest cách để thực hiện điều này chỉ đơn giản là kiểm tra các điểm mã Unicode cho mỗi ký tự trong chuỗi chống lại được biết đến biểu tượng cảm xúc và dingbats phạm vi, như vậy:

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
Một ví dụ mã như thế này là cách tốt hơn là đề xuất bao gồm phụ thuộc thư viện của bên thứ ba. Câu trả lời của Shardul là lời khuyên không khôn ngoan để làm theo — hãy luôn viết mã của riêng bạn.
thefaj

Này là rất tốt, cảm ơn bạn đã cho ý kiến những gì các trường hợp liên quan đến
Shawn Throop

1
Giống như rất nhiều mã của bạn, tôi đã triển khai nó trong một câu trả lời ở đây . Một điều tôi nhận thấy là nó bỏ lỡ một số biểu tượng cảm xúc, có lẽ vì họ không nằm trong các chuyên mục bạn liệt kê, ví dụ này: Robot Mặt emoji 🤖
Cue

1
@Tel Tôi đoán nó sẽ là phạm vi 0x1F900...0x1F9FF(theo Wikipedia). Không chắc tất cả phạm vi đều được coi là biểu tượng cảm xúc.
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
    }
}

Đây là bản sửa lỗi của tôi, với các phạm vi được cập nhật.


8

Swift 5.0

… Đã giới thiệu một cách mới để kiểm tra chính xác điều này!

Bạn phải đột nhập của bạn Stringvào nó Scalars. Mỗi Scalarcó một Propertygiá trị hỗ trợ isEmojigiá trị!

Trên thực tế, bạn thậm chí có thể kiểm tra xem Scalar có phải là công cụ sửa đổi Biểu tượng cảm xúc hay nhiều hơn. Kiểm tra tài liệu của Apple: https://developer.apple.com/documentation/swift/unicode/scalar/properties

Bạn có thể muốn xem xét kiểm tra isEmojiPresentationthay vì isEmoji, vì Apple tuyên bố như sau cho isEmoji:

Thuộc tính này đúng cho các đại lượng vô hướng được hiển thị dưới dạng biểu tượng cảm xúc theo mặc định và cũng cho các đại diện vô hướng có hiển thị biểu tượng cảm xúc không mặc định khi được theo sau bởi U + FE0F VARIATION SELECTOR-16. Điều này bao gồm một số vô hướng thường không được coi là biểu tượng cảm xúc.


Cách này thực sự chia Biểu tượng cảm xúc thành tất cả các công cụ sửa đổi, nhưng cách xử lý đơn giản hơn. Và vì Swift hiện tính các Biểu tượng cảm xúc với các bổ ngữ (ví dụ: 👨‍👩‍👧‍👦, 👨🏻‍💻, 🏴) là 1, bạn có thể làm tất cả các loại.

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 chỉ ra một cách thú vị để lấy tất cả các Biểu tượng cảm xúc:

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
Câu trả lời tuyệt vời, cảm ơn. Điều đáng nói là sdk tối thiểu của bạn phải là 10,2 để sử dụng phần này của Swift 5. Ngoài ra, để kiểm tra xem một chuỗi chỉ được tạo thành từ các biểu tượng cảm xúc, tôi phải kiểm tra xem nó có một trong những thuộc tính sau:scalar.properties.isEmoji scalar.properties.isEmojiPresentation scalar.properties.isEmojiModifier scalar.properties.isEmojiModifierBase scalar.properties.isJoinControl scalar.properties.isVariationSelector
A Springham

6
Lưu ý, các số nguyên 0-9 được coi là biểu tượng cảm xúc. Vì vậy, "6".unicodeScalars.first!.properties.isEmojisẽ đánh giá làtrue
Miniroo

6

Với Swift 5, giờ đây bạn có thể kiểm tra các thuộc tính unicode của từng ký tự trong chuỗi của bạn. Điều này cung cấp cho chúng tôi isEmojibiến thuận tiện trên mỗi chữ cái. Vấn đề là isEmojisẽ trả về true cho bất kỳ ký tự nào có thể được chuyển đổi thành biểu tượng cảm xúc 2 byte, chẳng hạn như 0-9.

Chúng tôi có thể xem xét biến isEmojivà cũng kiểm tra sự hiện diện của công cụ sửa đổi biểu tượng cảm xúc để xác định xem các ký tự không rõ ràng có hiển thị dưới dạng biểu tượng cảm xúc hay không.

Giải pháp này sẽ là bằng chứng trong tương lai nhiều hơn so với các giải pháp regex được cung cấp ở đây.

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

Cho chúng tôi

"hey".containsEmoji() //false

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

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

1
Và hơn thế nữa là Character("3️⃣").isEmoji // truetrong khiCharacter("3").isEmoji // false
Paul B

4

Swift 3 Lưu ý:

Có vẻ như cnui_containsEmojiCharactersphương thức đã bị xóa hoặc chuyển sang một thư viện động khác. _containsEmojivẫn nên làm việc mặc dù.

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:

Gần đây, tôi đã phát hiện ra một API riêng, trên NSStringđó có chức năng phát hiện xem một chuỗi có chứa ký tự Biểu tượng cảm xúc hay không:

let str: NSString = "hello😊"

Với một giao thức objc và unsafeBitCast:

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

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

Với valueForKey:

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

Với một chuỗi Swift thuần túy, bạn phải ép kiểu chuỗi như AnyObjecttrước khi sử dụng valueForKey:

let str = "hello😊"

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

Các phương thức được tìm thấy trong tệp tiêu đề NSString .


Đây là những gì tôi đang tìm kiếm, Cảm ơn JAL

Liệu điều này có bị Apple từ chối?
Andrey Chernukha

@AndreyChernukha Luôn có rủi ro, nhưng tôi chưa từng bị từ chối.
JAL

Không bao giờ sử dụng các API riêng tư. Tốt nhất, tổn thương sẽ chỉ đến vào ngày mai. Hoặc tháng sau.
xaphod

3

Bạn có thể sử dụng ví dụ mã này hoặc nhóm này .

Để sử dụng nó trong Swift, hãy nhập danh mục vào YourProject_Bridging_Header

#import "NSString+EMOEmoji.h"

Sau đó, bạn có thể kiểm tra phạm vi cho mọi biểu tượng cảm xúc trong Chuỗi của mình:

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

let containsEmoji: Bool = example.emo_containsEmoji()

    print(containsEmoji)

// Output: ["true"]

Tôi đã tạo một dự án ví dụ nhỏ với đoạn mã trên.


3

Bằng chứng trong tương lai: Kiểm tra thủ công các pixel của nhân vật; các giải pháp khác sẽ bị hỏng (và đã bị hỏng) khi các biểu tượng cảm xúc mới được thêm vào.

Lưu ý: Đây là Objective-C (có thể được chuyển đổi sang Swift)

Trong những năm qua, các giải pháp phát hiện biểu tượng cảm xúc này tiếp tục bị phá vỡ khi Apple thêm các biểu tượng cảm xúc mới với các phương pháp mới (như biểu tượng cảm xúc có màu da được xây dựng bằng cách chửi trước một ký tự với một ký tự bổ sung), v.v.

Cuối cùng tôi đã phá vỡ và chỉ viết phương pháp sau đây phù hợp với tất cả các biểu tượng cảm xúc hiện tại và sẽ hoạt động cho tất cả các biểu tượng cảm xúc trong tương lai.

Giải pháp tạo một UILabel với ký tự và nền đen. Sau đó CG sẽ chụp nhanh nhãn và tôi quét tất cả các pixel trong ảnh chụp nhanh để tìm bất kỳ pixel nào không phải màu đen đặc. Lý do tôi thêm nền đen là để tránh các vấn đề về màu sai do Kết xuất Subpixel

Giải pháp chạy RẤT nhanh trên thiết bị của tôi, tôi có thể kiểm tra hàng trăm ký tự mỗi giây, nhưng cần lưu ý rằng đây là giải pháp CoreGraphics và không nên được sử dụng nhiều như bạn có thể làm với phương pháp văn bản thông thường. Xử lý đồ họa nặng dữ liệu nên việc kiểm tra hàng nghìn ký tự cùng lúc có thể dẫn đến độ trễ đáng chú ý.

-(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
Tôi thích suy nghĩ của bạn! ;) - Ngoài cái hộp!
Ramon

Tại sao bạn lại làm điều này với chúng tôi? #apple #unicodestandard 😱🤔🤪🙈😈🤕💩
d4Rk

Tôi đã không nhìn vào điều này trong một thời gian nhưng tôi tự hỏi nếu tôi phải chuyển đổi sang UIColor sau đó sang hsb; Có vẻ như tôi chỉ có thể kiểm tra rằng r, g, b tất cả == 0? Nếu ai đó cố gắng hãy cho tôi biết
Albert Renshaw

tôi thích giải pháp này, nhưng nó sẽ không bị vỡ với một ký tự như ℹ?
Juan Carlos Ospina Gonzalez

1
@JuanCarlosOspinaGonzalez Không, trong biểu tượng cảm xúc hiển thị dưới dạng hộp màu xanh lam với chữ i màu trắng. Nó mang lại một điểm tốt mặc dù UILabel nên buộc phông chữ phải như vậy AppleColorEmoji, thêm rằng hiện tại là một sự cố an toàn, mặc dù tôi nghĩ Apple sẽ mặc định nó cho những điều đó
Albert Renshaw

2

Đối với Swift 3.0.2, câu trả lời sau là câu trả lời đơn giản nhất:

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

Câu trả lời hoàn toàn giống với những câu đã viết trước tôi, nhưng với bộ vô hướng biểu tượng cảm xúc được cập nhật.

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

Có một giải pháp tốt cho nhiệm vụ được đề cập. Nhưng Kiểm tra Unicode.Scalar: Các đặc tính của vô hướng unicode là tốt cho một Ký tự duy nhất. Và không đủ linh hoạt cho Chuỗi.

Thay vào đó, chúng ta có thể sử dụng Biểu thức chính quy - cách tiếp cận phổ biến hơn. Có một mô tả chi tiết về cách nó hoạt động dưới đây. Và đây là giải pháp.

Giải pháp

Trong Swift, bạn có thể kiểm tra xem một Chuỗi có phải là một ký tự Biểu tượng cảm xúc duy nhất hay không, bằng cách sử dụng tiện ích mở rộng có thuộc tính được tính toán như vậy:

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
    }

}

Cách hoạt động (chi tiết)

Một biểu tượng cảm xúc (glyph) có thể được tái tạo bằng một số biểu tượng, trình tự và sự kết hợp của chúng. Đặc tả Unicode xác định một số biểu diễn ký tự Emoji có thể có.

Biểu tượng cảm xúc một ký tự

Một ký tự Emoji được sao chép bởi một Scalar Unicode duy nhất.

Unicode định nghĩa Ký tự Biểu tượng cảm xúc là:

emoji_character := \p{Emoji}

Nhưng nó không nhất thiết có nghĩa là một nhân vật như vậy sẽ được vẽ dưới dạng Biểu tượng cảm xúc. Một ký hiệu số thông thường “1” có thuộc tính Biểu tượng cảm xúc là true, mặc dù nó vẫn có thể được vẽ dưới dạng văn bản. Và có một danh sách các ký hiệu như vậy: #, ©, 4, v.v.

Người ta nên nghĩ rằng chúng ta có thể sử dụng thuộc tính bổ sung để kiểm tra: “Emoji_Presentation”. Nhưng nó không hoạt động như thế này. Có một Biểu tượng cảm xúc như 🏟 hoặc 🛍, có thuộc tính Emoji_Presentation = false.

Để đảm bảo rằng ký tự được vẽ dưới dạng Biểu tượng cảm xúc theo mặc định, chúng ta nên kiểm tra danh mục của nó: nó phải là “Other_symbol”.

Vì vậy, trên thực tế, biểu thức chính quy cho Biểu tượng cảm xúc một ký tự phải được định nghĩa là:

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

Trình tự trình bày biểu tượng cảm xúc

Một ký tự, thường có thể được vẽ dưới dạng văn bản hoặc dưới dạng Biểu tượng cảm xúc. Sự xuất hiện của nó phụ thuộc vào một ký hiệu đặc biệt sau, một bộ chọn bản trình bày, cho biết kiểu trình bày của nó. \ x {FE0E} xác định biểu diễn văn bản. \ x {FE0F} xác định biểu tượng cảm xúc.

Danh sách các biểu tượng như vậy có thể được tìm thấy [tại đây] (
 https://unicode.org/Public/emoji/12.1/emoji-variation-sequences.txt ).

Unicode định nghĩa trình tự trình bày như sau:

emoji_presentation_sequence := emoji_character emoji_presentation_selector

Chuỗi biểu thức chính quy cho nó:

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

Trình tự keycap biểu tượng cảm xúc

Trình tự trông rất giống với Trình tự trình bày, nhưng nó có thêm đại lượng vô hướng ở cuối: \ x {20E3}. Phạm vi của các đại lượng vô hướng cơ sở có thể được sử dụng cho nó khá hẹp: 0-9 # * - và đó là tất cả. Ví dụ: 1️⃣, 8️⃣, * ️⃣.

Unicode định nghĩa chuỗi keycap như sau:

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

Biểu thức chính quy cho nó:

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

Trình tự sửa đổi biểu tượng cảm xúc

Một số biểu tượng cảm xúc có thể có ngoại hình đã sửa đổi như màu da. Ví dụ Emoji 🧑 có thể khác: 🧑🧑🏻🧑🏼🧑🏽🧑🏾🧑🏿. Để xác định Biểu tượng cảm xúc, được gọi là “Emoji_Modifier_Base” trong trường hợp này, người ta có thể sử dụng một “Emoji_Modifier” tiếp theo.

Nói chung, trình tự như vậy trông như thế này:

emoji_modifier_sequence := emoji_modifier_base emoji_modifier

Để phát hiện nó, chúng ta có thể tìm kiếm một chuỗi biểu thức chính quy:

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

Trình tự cờ biểu tượng cảm xúc

Cờ là biểu tượng cảm xúc với cấu trúc cụ thể của chúng. Mỗi lá cờ được biểu thị bằng hai biểu tượng “Regional_Indicator”.

Unicode định nghĩa chúng như sau:

emoji_flag_sequence := regional_indicator regional_indicator

Ví dụ: cờ của Ukraine 🇺🇦 trên thực tế được biểu thị bằng hai đại lượng vô hướng: \ u {0001F1FA \ u {0001F1E6}

Biểu thức chính quy cho nó:

emoji_flag_sequence := \p{RI}{2}

Trình tự thẻ biểu tượng cảm xúc (ETS)

Một trình tự sử dụng cái gọi là tag_base, theo sau là một đặc điểm kỹ thuật thẻ tùy chỉnh được bao gồm từ dải ký hiệu \ x {E0020} - \ x {E007E} và được kết thúc bằng tag_end mark \ x {E007F}.

Unicode định nghĩa nó như thế này:

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}

Điều kỳ lạ là Unicode cho phép thẻ dựa trên emoji_modifier_sequence hoặc emoji_presentation_sequence trong ED-14a . Nhưng đồng thời trong các biểu thức chính quy được cung cấp cùng một tài liệu, chúng dường như chỉ kiểm tra trình tự dựa trên một ký tự Emoji duy nhất.

Trong danh sách Biểu tượng cảm xúc Unicode 12.1, chỉ có ba Biểu tượng cảm xúc như vậy được định nghĩa. Tất cả chúng đều là cờ của các nước thuộc Vương quốc Anh: England 🏴󠁧󠁢󠁥󠁮󠁧󠁿, Scotland 🏴󠁧󠁢󠁳󠁣󠁴󠁿 và Wales 🏴󠁧󠁢󠁷󠁬󠁳󠁿. Và tất cả chúng đều dựa trên một ký tự Emoji duy nhất. Vì vậy, chúng ta chỉ nên kiểm tra một trình tự như vậy.

Biểu hiện thông thường:

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

Trình tự ghép nối có độ rộng bằng không biểu tượng cảm xúc (chuỗi ZWJ)

Bộ nối có độ rộng bằng 0 là một đại lượng vô hướng \ x {200D}. Với sự trợ giúp của nó, một số ký tự, vốn đã là Biểu tượng cảm xúc, có thể được kết hợp thành những ký tự mới.

Ví dụ: Biểu tượng cảm xúc “gia đình có cha, con trai và con gái” 👨‍👧‍👦 được tái tạo bằng sự kết hợp của cha 👨, con gái 👧 và con trai 👦 Biểu tượng cảm xúc được dán lại với nhau bằng các biểu tượng ZWJ.

Nó được phép gắn các yếu tố lại với nhau, đó là các ký tự Biểu tượng cảm xúc đơn, trình tự Trình bày và Trình tự sửa đổi.

Biểu thức chính quy cho chuỗi như vậy nói chung trông giống như sau:

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

Biểu thức chính quy cho tất cả chúng

Tất cả các biểu tượng Biểu tượng cảm xúc được đề cập ở trên có thể được mô tả bằng một biểu thức chính quy:

\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

tôi đã gặp vấn đề tương tự và cuối cùng đã tạo ra một StringCharacterphần mở rộng.

Mã quá dài để đăng vì nó thực sự liệt kê tất cả các biểu tượng cảm xúc (từ danh sách unicode chính thức v5.0) trong một mã CharacterSetbạn có thể tìm thấy tại đây:

https://github.com/piterwilson/StringEmoji

Hằng số

let emojiCharacterSet: CharacterSet

Bộ ký tự chứa tất cả các biểu tượng cảm xúc đã biết (như được mô tả trong Danh sách Unicode 5.0 chính thức http://unicode.org/emoji/charts-5.0/emoji-list.html )

Chuỗi

var isEmoji: Bool {get}

Đối Stringtượng có đại diện cho một ký tự Biểu tượng cảm xúc đã biết hay không

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

Đối tượng Stringcó chứa ký tự Biểu tượng cảm xúc đã biết hay không

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

Áp dụng a kCFStringTransformToUnicodeName- CFStringTransformtrên bản sao của Chuỗi

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

Trả về kết quả của a kCFStringTransformToUnicodeName- CFStringTransformvới \N{các tiền tố và }hậu tố bị xóa

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

Tính cách

var isEmoji: Bool {get}

Đối Charactertượng có đại diện cho một ký tự Biểu tượng cảm xúc đã biết hay không

print("".isEmoji) // false
print("😁".isEmoji) // true
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.