Swift - 用空格替换字符串中的表情符号

时间:2016-04-28 15:27:54

标签: string swift unicode emoji

我有一个检测字符串中url的方法,并返回url和可以找到它们的范围。一切都很完美,直到字符串上有表情符号。例如:

subcribe

由于表情符号,从文本中提取的网址为http://youtu.be/SW_d3fGz1而不是http://youtu.be/SW_d3fGz1hk。我认为最简单的解决方案是用空格字符替换字符串上的表情符号(因为我需要范围对于某些文本样式的东西是正确的)。问题是,使用Swift非常难以实现(很可能我的Swift String API缺乏能力)。

我一直试图这样做,但似乎我无法从unicode点数组中创建一个字符串:

"I'm gonna do this callenge as soon as I can swing again \n http://youtu.be/SW_d3fGz1hk"

我是否以错误的方式接近这个问题?取代表情符号是最好的解决方案吗?如果是这样,我该怎么做?

6 个答案:

答案 0 :(得分:6)

您可以使用模式匹配(用于表情符号模式)过滤掉String中的表情符号字符。

extension String {

    var emojilessStringWithSubstitution: String {
        let emojiPatterns = [UnicodeScalar(0x1F601)...UnicodeScalar(0x1F64F),
                             UnicodeScalar(0x2702)...UnicodeScalar(0x27B0)]
        return self.unicodeScalars
            .filter { ucScalar in !(emojiPatterns.contains{ $0 ~= ucScalar }) }
            .reduce("") { $0 + String($1) }
    }  
}

/* example usage */
let str = "I'm gonna do this callenge as soon as I can swing again \n http://youtu.be/SW_d3fGz1hk"
print(str.emojilessStringWithSubstitution)

/* I'm gonna do this callenge as soon as I can swing again
   http://youtu.be/SW_d3fGz1hk */

请注意,上述内容仅使用了问题中提供的表情符号间隔,并不代表所有表情符号,但该方法是通用的,可以通过在{{1}中包含额外的表情符号间隔来快速扩展。数组。

我意识到再次阅读你的问题,你更喜欢用空格字符替换表情符号,而不是删除它们(上面的过滤解决方案就是这样)。我们可以通过使用条件返回emojiPatterns操作替换上面的.filter操作来实现此目的,就像在您的问题中一样

.map

如上所述,根据你对这篇文章的评论(列出这些间隔),已经扩展了现有的表情符号间隔,这样表情符号检查现在可能是详尽无遗的。

答案 1 :(得分:3)

Swift 4:

extension String {
  func stringByRemovingEmoji() -> String {
    return String(self.filter { !$0.isEmoji() })
  }
}

extension Character {
  fileprivate func isEmoji() -> Bool {
    return Character(UnicodeScalar(UInt32(0x1d000))!) <= self && self <= Character(UnicodeScalar(UInt32(0x1f77f))!)
      || Character(UnicodeScalar(UInt32(0x2100))!) <= self && self <= Character(UnicodeScalar(UInt32(0x26ff))!)
  }
}

答案 2 :(得分:2)

快捷键5

请勿使用这种where方法来检测hardcoded。在emojis中,您可以轻松完成

Swift 5

答案 3 :(得分:0)

表情符号按Unicode分类为符号。字符集通常用于搜索操作。因此,我们将使用“字符集”作为符号的属性。

var emojiString =  "Hey there , welcome"
emojiString = emojiString.components(separatedBy: CharacterSet.symbols).joined()       
print(emojiString)

输出为

Hey there , welcome

现在观察表情符号被一个空格替换,因此有两个空格,我们通过以下方式替换它

emojiString.replacingOccurrences(of: "  ", with: " ") 

上述方法将“两个空白”的参数替换为“单个空白”

答案 4 :(得分:0)

我发现上述给出的解决方案不适用于某些字符,例如️‍♂️和。

要查找表情符号范围,我使用正则表达式将full list of emoji characters转换为只有十六进制值的文件。然后,我将它们转换为十进制格式并对其进行了排序。最后,我编写了一个脚本来查找范围。

这是isEmoji()的最终Swift扩展。

extension Character {

    func isEmoji() -> Bool {
        let emojiRanges = [
            (8205, 11093),
            (12336, 12953),
            (65039, 65039),
            (126980, 129685)
        ]
        let codePoint = self.unicodeScalars[self.unicodeScalars.startIndex].value
        for emojiRange in emojiRanges {
            if codePoint >= emojiRange.0 && codePoint <= emojiRange.1 {
                return true
            }
        }
        return false
    }

}

作为参考,这是我编写的python脚本,用于将十六进制字符串解析为整数,然后找到范围。

转换为十六进制的py

decimals = []
with open('hex.txt') as hexfile:
    for line in hexfile:
        num = int(line, 16)
        if num < 256:
            continue
        decimals.append(num)

decimals = list(set(decimals))
decimals.sort()

with open('decimal.txt', 'w') as decimalfile:
    for decimal in decimals:
        decimalfile.write(str(decimal) + "\n")

make-ranges.py

first_line = True
range_start = 0
prev = 0
with open('decimal.txt') as hexfile:
    for line in hexfile:
        if first_line: 
            prev = int(line)
            range_start = prev
            first_line = False
            continue

        curr = int(line)
        if prev + 1000 < curr: # 100 is abitrary to reduce number of ranges
            print("(" + str(range_start) + ", " + str(prev) + ")")
            range_start = curr
        prev = curr

答案 5 :(得分:0)

获取所有表情符号比您想象的要复杂。有关如何确定哪些字符是表情符号的更多信息,请查看此stackoverflow帖子或this article

基于这些信息,我建议使用Character上的扩展名,以使我们更容易理解哪些字符是表情符号。然后添加一个字符串扩展名,以轻松地将找到的表情符号替换为另一个字符。

extension Character {
   var isSimpleEmoji: Bool {
      guard let firstProperties = unicodeScalars.first?.properties else {
        return false
      }
      return unicodeScalars.count == 1 &&
          (firstProperties.isEmojiPresentation ||
             firstProperties.generalCategory == .otherSymbol)
   }
   var isCombinedIntoEmoji: Bool {
      return unicodeScalars.count > 1 &&
             unicodeScalars.contains {
                $0.properties.isJoinControl ||
                $0.properties.isVariationSelector
             }
   }
   var isEmoji: Bool {
      return isSimpleEmoji || isCombinedIntoEmoji
   }
}

extension String {
    func replaceEmoji(with character: Character) -> String {
        return String(map { $0.isEmoji ? character : $0 })
    }
}

使用它将变成:

"Some string ??? with emoji".replaceEmoji(with: " ")