找出“字符串中的字符”是否为表情符号?


90

我需要找出字符串中的字符是否是表情符号。

例如,我有这个角色:

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

我需要确定该字符是否为表情符号。


我很好奇:您为什么需要这些信息?
Martin R

@EricD .:有许多Unicode字符占用一个以上的UTF-8代码点(例如“€” = E2 82 AC)或一个以上的UTF-16代码点(例如“𝄞” = D834 DD1E)。
Martin R

希望您能从此obj-c版本的代码stackoverflow.com/questions/19886642/…中
Ashish Kakkad 2015年

字符串具有其索引,这是使用它们的首选方式。要获取特定字符(或字素簇),您可以:let character = string[string.index(after: string.startIndex)]let secondCharacter = string[string.index(string.startIndex, offsetBy: 1)]
Paul B

Answers:


228

我偶然发现的是字符,unicode标量和字形之间的区别。

例如,字形👨‍👨‍👧‍👧由7个unicode标量组成:

  • 四个表情符号:👨👩👧👧
  • 每个表情符号之间都有一个特殊字符,其作用类似于字符胶;查看规格以获取更多信息

另一个示例,字形of由2个unicode标量组成:

  • 常规表情符号:👌
  • 肤色调节剂:🏿

最后一个字形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-我想是因为它更正确,在我的测试中,它对于包含混合字符的字符串返回了true)。
蒂姆·布尔

3
我的糟糕,我更改了一些代码,猜想我搞砸了。我更新了示例
Kevin R

2
@Andrew:当然,我在示例中添加了另一种方法来演示:)。
凯文R

2
@Andrew,这里真的很混乱。我添加了一个示例,说明了如何做到这一点。问题是我假设要知道CoreText将如何通过简单地检查字符来呈现字形。如果有人对清洁方法的建议,请告诉我。
凯文R

3
@Andrew感谢您指出,我更改了containsOnlyEmoji检查方式。我还将示例更新为Swift 3.0。
凯文R

48

最简单,最干净,最快捷的方法是按照已知的emoji和dingbats范围检查字符串中每个字符的Unicode代码点,如下所示:

extension String {

    var containsEmoji: Bool {
        for scalar in unicodeScalars {
            switch scalar.value {
            case 0x1F600...0x1F64F, // Emoticons
                 0x1F300...0x1F5FF, // Misc Symbols and Pictographs
                 0x1F680...0x1F6FF, // Transport and Map
                 0x2600...0x26FF,   // Misc symbols
                 0x2700...0x27BF,   // Dingbats
                 0xFE00...0xFE0F,   // Variation Selectors
                 0x1F900...0x1F9FF, // Supplemental Symbols and Pictographs
                 0x1F1E6...0x1F1FF: // Flags
                return true
            default:
                continue
            }
        }
        return false
    }

}

9
这样的代码示例比建议包含第三方库依赖项的方式要好得多。Shardul的答案是遵循不明智的建议-始终编写自己的代码。
thefaj

太好了,谢谢您对案件的相关评论
Shawn Throop

1
就像您的代码一样,我在这里的答案中实现了它。我注意到的是,它会错过一些表情图案,也许是因为他们是不是你所列类别的一部分,比如这一个:机器人面部表情图案🤖
线索

1
@电话我想这将是范围0x1F900...0x1F9FF(每个维基百科)。不确定是否应将所有范围都视为表情符号。
Frizlab '16

8
extension String {
    func containsEmoji() -> Bool {
        for scalar in unicodeScalars {
            switch scalar.value {
            case 0x3030, 0x00AE, 0x00A9,// Special Characters
            0x1D000...0x1F77F,          // Emoticons
            0x2100...0x27BF,            // Misc symbols and Dingbats
            0xFE00...0xFE0F,            // Variation Selectors
            0x1F900...0x1F9FF:          // Supplemental Symbols and Pictographs
                return true
            default:
                continue
            }
        }
        return false
    }
}

这是我的解决方法,具有更新的范围。


8

斯威夫特5.0

…引入了一种新的检查方法!

您必须闯入StringScalars。每个Scalar都有一个Property支持该isEmoji价值的价值!

实际上,您甚至可以检查Scalar是否是Emoji修改器或更多。查看Apple文档:https : //developer.apple.com/documentation/swift/unicode/scalar/properties

您可能需要考虑检查isEmojiPresentation而不是isEmoji,因为Apple指出以下内容isEmoji

此属性对于默认情况下渲染为表情符号的标量以及在后跟U + FE0F VARIATION SELECTOR-16时具有非默认表情符号渲染的标量均适用。这包括一些通常不被视为表情符号的标量。


这种方法实际上将表情符号拆分为所有修饰符,但处理起来更简单。随着Swift现在将带有修饰符(例如👨‍👩‍👧‍👦,👨🏻‍💻,🏴)的Emoji计数为1,您可以进行各种处理。

var string = "🤓 test"

for scalar in string.unicodeScalars {
    let isEmoji = scalar.properties.isEmoji

    print("\(scalar.description) \(isEmoji)"))
}

// 🤓 true
//   false
// t false
// e false
// s false
// t false

NSHipster指出了一种获取所有表情符号的有趣方法:

import Foundation

var emoji = CharacterSet()

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

    // Implemented in Swift 5 (SE-0221)
    // https://github.com/apple/swift-evolution/blob/master/proposals/0221-character-properties.md
    if scalarValue.properties.isEmoji {
        emoji.insert(scalarValue)
    }
}

1
好答案,谢谢。值得一提的是,要使用Swift 5的这一部分,您的最小sdk必须为10.2。此外,为了检查字符串是否仅由表情符号组成,我必须检查其是否具有以下属性之一:scalar.properties.isEmoji scalar.properties.isEmojiPresentation scalar.properties.isEmojiModifier scalar.properties.isEmojiModifierBase scalar.properties.isJoinControl scalar.properties.isVariationSelector
Springham

6
当心,整数0-9被视为表情符号。所以"6".unicodeScalars.first!.properties.isEmoji将评估为true
Miniroo

6

使用Swift 5,您现在可以检查字符串中每个字符的unicode属性。这给我们isEmoji每个字母方便的变量。问题是,isEmoji对于任何可以转换为2字节表情符号的字符,例如0-9,都会返回true。

我们可以查看变量isEmoji,还可以检查是否存在表情符号修饰符,以确定模糊字符是否将显示为表情符号。

与此处提供的正则表达式解决方案相比,该解决方案应具有更多的未来证明。

extension String {
    func containsOnlyEmojis() -> Bool {
        if count == 0 {
            return false
        }
        for character in self {
            if !character.isEmoji {
                return false
            }
        }
        return true
    }
    
    func containsEmoji() -> Bool {
        for character in self {
            if character.isEmoji {
                return true
            }
        }
        return false
    }
}

extension Character {
    // An emoji can either be a 2 byte unicode character or a normal UTF8 character with an emoji modifier
    // appended as is the case with 3️⃣. 0x238C is the first instance of UTF16 emoji that requires no modifier.
    // `isEmoji` will evaluate to true for any character that can be turned into an emoji by adding a modifier
    // such as the digit "3". To avoid this we confirm that any character below 0x238C has an emoji modifier attached
    var isEmoji: Bool {
        guard let scalar = unicodeScalars.first else { return false }
        return scalar.properties.isEmoji && (scalar.value > 0x238C || unicodeScalars.count > 1)
    }
}

给我们

"hey".containsEmoji() //false

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

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

1
而且更重要的是Character("3️⃣").isEmoji // true,同时Character("3").isEmoji // false
保罗乙

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该API上公开了用于检测字符串是否包含表情符号字符的功能:

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头文件中找到的方法。


这就是我在寻找的东西,谢谢日航

苹果会拒绝吗?
安德烈·切尔努卡

@AndreyChernukha总是有风险,但是我还没有遇到过任何拒绝。
2016年

永远不要使用私有API。最好的情况是明天才会受伤。或下个月。
xaphod

3

您可以使用此代码示例或此pod

要在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)

多年来,随着苹果添加带有新方法的新表情符号(例如通过将其他字符预先模制一个字符而制成的肤色表情符号)等,这些检测表情符号的解决方案不断突破。

我终于崩溃了,只写了下面的方法,该方法适用于所有当前的表情符号,并且应该适用于所有未来的表情符号。

该解决方案创建一个带有字符和黑色背景的UILabel。CG然后为标签拍摄快照,然后我扫描快照中的所有像素以查找任何非纯黑色像素。我添加黑色背景的原因是为了避免由于子像素渲染导致的伪色问题

该解决方案在我的设备上运行非常快,我每秒可以检查数百个字符,但是应该注意,这是一个CoreGraphics解决方案,不应像常规文本方法那样大量使用。图形处理的数据量很大,因此一次检查数千个字符可能会导致明显的延迟。

-(BOOL)isEmoji:(NSString *)character {
    
    UILabel *characterRender = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
    characterRender.text = character;
    characterRender.font = [UIFont fontWithName:@"AppleColorEmoji" size:12.0f];//Note: Size 12 font is likely not crucial for this and the detector will probably still work at an even smaller font size, so if you needed to speed this checker up for serious performance you may test lowering this to a font size like 6.0
    characterRender.backgroundColor = [UIColor blackColor];//needed to remove subpixel rendering colors
    [characterRender sizeToFit];
    
    CGRect rect = [characterRender bounds];
    UIGraphicsBeginImageContextWithOptions(rect.size,YES,0.0f);
    CGContextRef contextSnap = UIGraphicsGetCurrentContext();
    [characterRender.layer renderInContext:contextSnap];
    UIImage *capturedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    CGImageRef imageRef = [capturedImage CGImage];
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
    NSUInteger bytesPerPixel = 4;//Note: Alpha Channel not really needed, if you need to speed this up for serious performance you can refactor this pixel scanner to just RGB
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;
    CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                                                 bitsPerComponent, bytesPerRow, colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);
    
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
    CGContextRelease(context);
    
    BOOL colorPixelFound = NO;
    
    int x = 0;
    int y = 0;
    while (y < height && !colorPixelFound) {
        while (x < width && !colorPixelFound) {
            
            NSUInteger byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
            
            CGFloat red = (CGFloat)rawData[byteIndex];
            CGFloat green = (CGFloat)rawData[byteIndex+1];
            CGFloat blue = (CGFloat)rawData[byteIndex+2];
            
            CGFloat h, s, b, a;
            UIColor *c = [UIColor colorWithRed:red green:green blue:blue alpha:1.0f];
            [c getHue:&h saturation:&s brightness:&b alpha:&a];//Note: I wrote this method years ago, can't remember why I check HSB instead of just checking r,g,b==0; Upon further review this step might not be needed, but I haven't tested to confirm yet. 
            
            b /= 255.0f;
            
            if (b > 0) {
                colorPixelFound = YES;
            }
            
            x++;
        }
        x=0;
        y++;
    }
    
    return colorPixelFound;
    
}

4
我喜欢你的想法!;) - 盒子外面!
拉蒙

您为什么要对我们这样做?#apple #unicodestandard😱🤔🤪🙈😈🤕💩–
d4Rk

我已经有一段时间没有看过这个了,但是我想知道是否我必须先转换为UIColor然后再转换为hsb。看来我可以检查r,g,b全部== 0吗?如果有人尝试让我知道
Albert Renshaw

我喜欢这种解决方案,但不会以ℹ这样的角色打断吗?
Juan Carlos Ospina Gonzalez,

1
@JuanCarlosOspinaGonzalez Nope,在表情符号中呈现为带有白色i的蓝色框。它确实提出了一个很好的观点,尽管UILabel应该强制将字体设置为AppleColorEmoji,并且现在将其作为故障保护添加,尽管我认为苹果还是会默认使用这些字体
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
        }
    }

}


0

对于上述任务有一个很好的解决方案。但是检查Unicode标量的Unicode.Scalar.Properties对于单个字符是有好处的。而且对于Strings不够灵活。

我们可以改用正则表达式-更通用的方法。以下是其工作方式的详细说明。解决方案就在这里。

解决方案

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

}

工作原理(详细信息)

单个表情符号(字形)可以通过许多不同的符号,序列及其组合来复制。 Unicode规范定义了几种可能的表情符号字符表示形式。

单字符表情符号

由单个Unicode标量复制的Emoji字符。

Unicode将表情符号字符定义为:

emoji_character := \p{Emoji}

但这并不一定意味着将此类字符绘制为表情符号。普通数字符号“ 1”的Emoji表情属性为true,尽管它仍可能会绘制为文本。并且有这样的符号列表:#,©,4等。

应该认为,我们可以使用其他属性来检查:“ Emoji_Presentation”。但这不是这样的。有一个像🏟或🛍这样的表情符号,其属性为Emoji_Presentation = false。

为确保默认情况下将字符绘制为表情符号,我们应检查其类别:应为“ Other_symbol”。

因此,实际上,单字符表情符号的正则表达式应定义为:

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

表情符号展示顺序

字符,通常可以绘制为文本或表情符号。它的外观取决于特殊的跟随符号,即表示选择器,它指示其表示类型。\ x {FE0E}定义文本表示。\ x {FE0F}定义表情符号表示。

此类符号的列表可以在[here](
 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}

表情符号按键序列

该序列看起来与Presentation序列非常相似,但是在末尾具有附加的标量:\ 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_Modifier_Base”),可以使用后续的“ Emoji_Modifier”。

通常,这样的顺序如下:

emoji_modifier_sequence := emoji_modifier_base emoji_modifier

为了检测它,我们可以搜索一个正则表达式序列:

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

表情符号标志序列

标志是具有特定结构的表情符号。每个标志都用两个“ 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标记\ 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允许标签基于ED-14a中的emoji_modifier_sequence或emoji_presentation_sequence 。但是同时,在相同文档中提供的正则表达式中,它们似乎仅根据单个表情符号字符来检查序列。

在Unicode 12.1表情符号列表中,仅定义了三种此类表情符号。它们都是英国国家的国旗:英格兰,苏格兰和威尔士。所有这些都基于单个表情符号字符。因此,我们最好只检查这样的序列。

正则表达式:

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

表情符号零宽度连接符序列(ZWJ序列)

零宽度连接符是标量\ x {200D}。在它的帮助下,可以将几个本身已经是表情符号的字符组合成新的字符。

例如,“父亲,儿子和女儿的家庭”表情符号👨是通过将父亲👨,女儿👧和儿子👦表情符号与ZWJ符号粘合在一起的组合来复制的。

允许将元素(单个表情符号字符,表示和修饰符序列)粘贴在一起。

这种序列的正则表达式通常如下所示:

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

我遇到了同样的问题,最终提出了StringCharacter扩展。

该代码过长,无法发布,因为它实际上列出了所有表情符号(来自官方unicode列表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:布尔{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}

在字符串的副本上应用kCFStringTransformToUnicodeName-CFStringTransform

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

返回的结果kCFStringTransformToUnicodeName-CFStringTransform\N{前缀和}后缀去掉

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

字符

var isEmoji:布尔{get}

Character实例是否代表已知的表情符号字符

print("".isEmoji) // false
print("😁".isEmoji) // true
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.