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

And*_*rew 68 string character ios emoji swift

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

例如,我有这个角色:

let string = ""
let character = Array(string)[0]
Run Code Online (Sandbox Code Playgroud)

我需要弄清楚那个角色是不是表情符号.

Kev*_*n R 169

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

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

  • 四个表情符号字符:
  • 在每个表情符号之间是一个特殊字符,它的作用就像字符胶水; 有关详细信息,请参阅规格

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

  • 常规表情符号:
  • 肤色修饰符:

因此,在渲染字符时,生成的字形确实很重要.

我正在寻找的是一种检测字符串是否完全且只有一个表情符号的方法.所以我可以渲染它比普通文本更大(就像iOS10和WhatsApp现在做的消息一样).如上所述,字符数实际上没用.('胶水字符'也不被视为表情符号).

你可以做的是使用CoreText来帮助你将字符串分解为字形并计算它们.此外,我将把阿诺德和塞巴斯蒂安洛佩兹提出的部分延期移到另一个延期1.

它会留下以下结果:

extension Character {
    /// A simple emoji is one scalar and presented to the user as an Emoji
    var isSimpleEmoji: Bool {
        guard let firstProperties = unicodeScalars.first?.properties else {
            return false
        }
        return unicodeScalars.count == 1 &&
            (firstProperties.isEmojiPresentation ||
                firstProperties.generalCategory == .otherSymbol)
    }

    /// Checks if the scalars will be merged into an emoji
    var isCombinedIntoEmoji: Bool {
        return unicodeScalars.count > 1 &&
            unicodeScalars.contains { $0.properties.isJoinControl || $0.properties.isVariationSelector }
    }

    var isEmoji: Bool {
        return isSimpleEmoji || isCombinedIntoEmoji
    }
}

extension String {
    var isSingleEmoji: Bool {
        return count == 1 && containsEmoji
    }

    var containsEmoji: Bool {
        return contains { $0.isEmoji }
    }

    var containsOnlyEmoji: Bool {
        return !isEmpty && !contains { !$0.isEmoji }
    }

    var emojiString: String {
        return emojis.map { String($0) }.reduce("", +)
    }

    var emojis: [Character] {
        return filter { $0.isEmoji }
    }

    var emojiScalars: [UnicodeScalar] {
        return filter{ $0.isEmoji }.flatMap { $0.unicodeScalars }
    }
}
Run Code Online (Sandbox Code Playgroud)

哪个会给你以下结果:

"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
"???".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
Run Code Online (Sandbox Code Playgroud)

  • 这是迄今为止最好和最正确的答案.谢谢!一个小注释,你的例子与代码不匹配(你在代码片段中重命名containsOnlyEmoki到containsEmoji - 我认为因为它更正确,在我的测试中它对于带有混合字符的字符串返回true). (4认同)
  • @Andrew感谢你指出这一点,我改变了`containsOnlyEmoji`检查的方式.我还将示例更新为Swift 3.0. (3认同)
  • 我的坏,我改变了一些代码,猜猜我搞砸了.我更新了这个例子 (2认同)
  • @Andrew:当然,我在示例中添加了另一种方法来演示这个:). (2认同)
  • @Andrew这是它变得非常混乱的地方.我添加了一个如何做到这一点的例子.问题是我假设知道CoreText如何通过简单地检查字符来呈现字形.如果有人有更清洁方法的建议,请告诉我. (2认同)
  • 我还添加了比较:`$ 0.properties.generalCategory == .otherSymbol`,使其可用于更多表情符号,例如⏰等。 (2认同)

Arn*_*old 43

最简单,最简洁,最快捷的方法是简单地检查字符串中每个字符的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
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 像这样的代码示例比建议包含第三方库依赖项更好.Shardul的回答是不明智的建议 - 始终编写自己的代码. (5认同)
  • 就像你的代码一样,我在一个答案中实现了它 [here](http://stackoverflow.com/questions/37766611/how-to-replace-emoji-characters-with-their-descriptions-in-a-swift-细绳)。我注意到的一件事是它错过了一些表情符号,可能是因为它们不属于您列出的类别,例如这个:机器人脸表情符号 (2认同)

Min*_*roo 9

使用 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

给我们

"hey".containsEmoji() //false

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

"3".containsEmoji() //false
"3??".containsEmoji() //true
Run Code Online (Sandbox Code Playgroud)


小智 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
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的修复,更新范围.


Sta*_*ith 8

Swift 5解决方案使用使用标量的适用于文本、笑脸、心形表情符号 \xe2\x9d\xa4\xef\xb8\x8f\xe2\x9d\xa4\xef\xb8\x8f\xe2\x80\x8d 和数字 0\xef \xb8\x8f\xe2\x83\xa3 1 2 3 等

\n

Swift 5标量isEmojiisEmojiPresentation属性,有助于在特定的字符串中查找表情符号。

\n
\n

是表情符号 - 布尔值,指示标量是否具有表情符号呈现,无论它是否是默认的。

\n

isEmojiPresentation - 一个布尔值,指示默认情况下标量是否应该使用表情符号呈现而不是文本呈现呈现。

\n
\n

正如您从这些定义中看到的,我们不能只在字符串的标量上使用isEmojior - 这不会告诉我们这个标量是否真的是表情符号isEmojiPresentation

\n

幸运的是,苹果给了我们一个线索:

\n
\n

仅对单个标量进行测试isEmoji不足以\n确定文本单元是否呈现为表情符号;正确的测试\n需要检查Character. 除了检查基本标量是否具有 之外isEmoji == true,您还必须检查其默认表示形式(请参阅isEmojiPresentation)并确定其后面是否跟随会修改表示形式的变体选择器。

\n
\n

最后,这是我的实现,适用于数字、笑脸、文本和 \xe2\x9d\xa4\xef\xb8\x8f 符号:

\n
import Foundation\n\nextension String {\n\n    func containsEmoji() -> Bool {\n        \n        for character in self {\n            var shouldCheckNextScalar = false\n            for scalar in character.unicodeScalars {\n               if shouldCheckNextScalar {\n                    if scalar == "\\u{FE0F}" { // scalar that indicates that character should be displayed as emoji\n                        return true\n                    }\n                    shouldCheckNextScalar = false\n                }\n                \n                if scalar.properties.isEmoji {\n                    if scalar.properties.isEmojiPresentation {\n                        return true\n                    }\n                    shouldCheckNextScalar = true\n                }\n            }\n        }\n        \n        return false\n    }\n    \n}\n
Run Code Online (Sandbox Code Playgroud)\n

测试:

\n
"hello \xe2\x9d\xa4\xef\xb8\x8f".containsEmoji()   // true\n"1234567890".containsEmoji() // false\n"numero 0\xef\xb8\x8f\xe2\x83\xa3".containsEmoji()  // true\n"abcde".containsEmoji()      // false\n"panda ".containsEmoji()   // true\n
Run Code Online (Sandbox Code Playgroud)\n


ale*_*ner 7

斯威夫特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
Run Code Online (Sandbox Code Playgroud)

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 当心,整数0-9被视为表情符号。因此,“ 6” .unicodeScalars.first!.properties.isEmoji`的评估结果为“ true” (3认同)
  • ❤️ 有两个标量。第一个标量的“isEmoji”为“true”,但“isEmojiPresentation”为“false”。第二个标量只会为“isVariationSelector”返回“true”。所以看起来不像是理解什么是表情符号的直接​​方法 (3认同)
  • 还有其他字符,如“#”和“*”,对于“isEmoji”检查也会返回 true。`isEmojiPresentation` 似乎工作得更好,至少它会为 `0...9`、`#`、`*` 以及我可以在美国英语键盘上尝试的任何其他符号返回 `false`。任何人都有更多的经验并且知道它是否可以信任输入验证? (2认同)

小智 5

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

\n\n

我们可以使用正则表达式来代替\xe2\x80\x94 更通用的方法。下面有对其工作原理的详细描述。解决方案就在这里。

\n\n

解决方案

\n\n

在 Swift 中,您可以使用具有此类计算属性的扩展来检查字符串是否是单个表情符号字符:

\n\n
extension String {\n\n    var isSingleEmoji : Bool {\n        if self.count == 1 {\n            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}]))*"\n\n            let fullRange = NSRange(location: 0, length: self.utf16.count)\n            if let regex = try? NSRegularExpression(pattern: emodjiGlyphPattern, options: .caseInsensitive) {\n                let regMatches = regex.matches(in: self, options: NSRegularExpression.MatchingOptions(), range: fullRange)\n                if regMatches.count > 0 {\n                    // if any range found \xe2\x80\x94 it means, that that single character is emoji\n                    return true\n                }\n            }\n        }\n        return false\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

它是如何工作的(详细)

\n\n

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

\n\n

单字符表情符号

\n\n

由单个 Unicode 标量再现的表情符号字符。

\n\n

Unicode 将表情符号定义为:

\n\n
emoji_character := \\p{Emoji}\n
Run Code Online (Sandbox Code Playgroud)\n\n

但这并不一定意味着这样的字符将被绘制为表情符号。普通数字符号 \xe2\x80\x9c1\xe2\x80\x9d 的 Emoji 属性为 true,但仍可能被绘制为文本。并且有一个此类符号的列表:#、\xc2\xa9、4 等。

\n\n

人们应该想到,我们可以使用附加属性来检查:\xe2\x80\x9cEmoji_Presentation\xe2\x80\x9d。但\xe2\x80\x99 并不是这样工作的。有一个类似于 或 的表情符号,其属性 Emoji_Presentation=false。

\n\n

为了确保该字符默认绘制为表情符号,我们应该检查其类别:它应该是 \xe2\x80\x9cOther_symbol\xe2\x80\x9d。

\n\n

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

\n\n
emoji_character := \\p{Emoji}&&\\p{Other_symbol}\n
Run Code Online (Sandbox Code Playgroud)\n\n

表情符号呈现顺序

\n\n

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

\n\n

此类符号的列表可以在[此处](\xe2\x80\xa8 https://unicode.org/Public/emoji/12.1/emoji-variation-sequences.txt )找到。

\n\n

Unicode 定义表示顺序如下:

\n\n
emoji_presentation_sequence := emoji_character emoji_presentation_selector\n
Run Code Online (Sandbox Code Playgroud)\n\n

它的正则表达式序列:

\n\n
emoji_presentation_sequence := \\p{Emoji} \\x{FE0F}\n
Run Code Online (Sandbox Code Playgroud)\n\n

表情符号键帽序列

\n\n

该序列看起来与演示序列非常相似,但它在末尾有一个额外的标量:\\x{20E3}。用于它的可能基标量的范围相当窄:0-9#* \xe2\x80\x94 和 \xe2\x80\x99 全部。示例:1\xef\xb8\x8f\xe2\x83\xa3、8\xef\xb8\x8f\xe2\x83\xa3、*\xef\xb8\x8f\xe2\x83\xa3。

\n\n

Unicode 定义键帽序列如下:

\n\n
emoji_keycap_sequence := [0-9#*] \\x{FE0F 20E3}\n
Run Code Online (Sandbox Code Playgroud)\n\n

它的正则表达式:

\n\n
emoji_keycap_sequence := \\p{Emoji} \\x{FE0F} \\x{FE0F}\n
Run Code Online (Sandbox Code Playgroud)\n\n

表情符号修饰符序列

\n\n

有些表情符号可以修改外观,例如肤色。例如,表情符号可以不同: 。要定义表情符号(在本例中称为 \xe2\x80\x9cEmoji_Modifier_Base\xe2\x80\x9d),可以使用后续的 \xe2\x80\x9cEmoji_Modifier\xe2\x80\x9d。

\n\n

一般来说,这样的序列看起来像这样:

\n\n
emoji_modifier_sequence := emoji_modifier_base emoji_modifier\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
emoji_modifier_sequence := \\p{Emoji} \\p{EMod}\n
Run Code Online (Sandbox Code Playgroud)\n\n

表情符号标志序列

\n\n

旗帜是具有特定结构的表情符号。每个标志由两个 \xe2\x80\x9cRegional_Indicator\xe2\x80\x9d 符号表示。

\n\n

Unicode 将它们定义为:

\n\n
emoji_flag_sequence := regional_indicator regional_indicator\n
Run Code Online (Sandbox Code Playgroud)\n\n

例如,乌克兰国旗实际上用两个标量表示: \\u{0001F1FA \\u{0001F1E6}

\n\n

它的正则表达式:

\n\n
emoji_flag_sequence := \\p{RI}{2}\n
Run Code Online (Sandbox Code Playgroud)\n\n

表情符号标签序列 (ETS)

\n\n

使用所谓的 tag_base 的序列,后跟由符号 \\x{E0020}-\\x{E007E} 范围组成的自定义标签规范,并以 tag_end 标记 \\x{E007F} 结束。

\n\n

Unicode 是这样定义的:

\n\n
emoji_tag_sequence := tag_base tag_spec tag_end\ntag_base\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0:= emoji_character\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 | emoji_modifier_sequence\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 | emoji_presentation_sequence\ntag_spec\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0:= [\\x{E0020}-\\x{E007E}]+\ntag_end\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0:= \\x{E007F}\n
Run Code Online (Sandbox Code Playgroud)\n\n

奇怪的是,Unicode 允许标签基于ED-14a中的 emoji_modifier_sequence 或 emoji_presentation_sequence 。但同时在同一文档提供的正则表达式中,他们似乎仅基于单个表情符号字符检查序列。

\n\n

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

\n\n

正则表达式:

\n\n
\\p{Emoji} [\\x{E0020}-\\x{E007E}]+ \\x{E007F}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n

例如,具有父亲、儿子和女儿的 \xe2\x80\x9cfamily\xe2\x80\x9d 表情符号 \xe2\x80\x8d\xe2\x80\x8d 是由父亲、女儿和儿子表情符号粘合在一起的组合再现的ZWJ 符号。

\n\n

允许将元素粘在一起,这些元素是单个表情符号字符、表示和修饰符序列。

\n\n

此类序列的正则表达式通常如下所示:

\n\n
emoji_zwj_sequence := emoji_zwj_element (\\x{200d} emoji_zwj_element )+\n
Run Code Online (Sandbox Code Playgroud)\n\n

所有这些的正则表达式

\n\n

所有上述的表情符号表示都可以用一个正则表达式来描述:

\n\n
\\p{RI}{2}\n| ( \\p{Emoji} \n    ( \\p{EMod} \n    | \\x{FE0F}\\x{20E3}? \n    | [\\x{E0020}-\\x{E007E}]+\\x{E007F} \n    ) \n  | \xe2\x80\xa8[\\p{Emoji}&&\\p{Other_symbol}] \n  )\n  ( \\x{200D}\n    ( \\p{Emoji} \n      ( \\p{EMod} \n      | \\x{FE0F}\\x{20E3}? \n      | [\\x{E0020}-\\x{E007E}]+\\x{E007F} \n      ) \n    | [\\p{Emoji}&&\\p{Other_symbol}] \n    ) \n  )*\n
Run Code Online (Sandbox Code Playgroud)\n