Swift - 用空格替换字符串中的表情符号
Swift - Replacing emojis in a string with whitespace
我有一种方法可以检测字符串中的 urls 并且 returns 我可以检测到 urls 和可以找到它们的范围。一切正常,直到字符串上出现表情符号。例如:
"I'm gonna do this callenge as soon as I can swing again \n http://youtu.be/SW_d3fGz1hk"
由于表情符号,从文本中提取的 url 是 http://youtu.be/SW_d3fGz1 instead of http://youtu.be/SW_d3fGz1hk。我认为最简单的解决方案是将字符串中的表情符号替换为空白字符(因为我需要正确的范围来处理某些文本样式)。问题是,使用 Swift 很难做到这一点(很可能我缺乏使用 Swift 字符串 API 的能力)。
我一直在尝试这样做,但似乎无法从 unicode 点数组创建字符串:
var emojilessStringWithSubstitution: String {
let emojiRanges = [0x1F601...0x1F64F, 0x2702...0x27B0]
let emojiSet = Set(emojiRanges.flatten())
let codePoints: [UnicodeScalar] = self.unicodeScalars.map {
if emojiSet.contains(Int([=12=].value)) {
return UnicodeScalar(32)
}
return [=12=]
}
return String(codePoints)
}
我是不是以错误的方式解决了这个问题?在这里替换表情符号是最好的解决方案吗?如果可以,我该怎么做?
您可以使用模式匹配(针对表情符号模式)从您的 String
中过滤掉表情符号字符。
extension String {
var emojilessStringWithSubstitution: String {
let emojiPatterns = [UnicodeScalar(0x1F601)...UnicodeScalar(0x1F64F),
UnicodeScalar(0x2702)...UnicodeScalar(0x27B0)]
return self.unicodeScalars
.filter { ucScalar in !(emojiPatterns.contains{ [=10=] ~= ucScalar }) }
.reduce("") { [=10=] + String() }
}
}
/* 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 */
请注意,以上仅使用问题中提供的表情符号间隔,并不代表所有表情符号,但该方法是通用的,可以通过在 emojiPatterns
数组.
我意识到再次阅读你的问题,你更喜欢用空白字符替换表情符号,而不是删除它们(上面的过滤解决方案就是这样做的)。我们可以通过用条件 return .map
操作替换上面的 .filter
操作来实现这一点,就像您的问题
extension String {
var emojilessStringWithSubstitution: String {
let emojiPatterns = [UnicodeScalar(0x1F600)...UnicodeScalar(0x1F64F),
UnicodeScalar(0x1F300)...UnicodeScalar(0x1F5FF),
UnicodeScalar(0x1F680)...UnicodeScalar(0x1F6FF),
UnicodeScalar(0x2600)...UnicodeScalar(0x26FF),
UnicodeScalar(0x2700)...UnicodeScalar(0x27BF),
UnicodeScalar(0xFE00)...UnicodeScalar(0xFE0F)]
return self.unicodeScalars
.map { ucScalar in
emojiPatterns.contains{ [=11=] ~= ucScalar } ? UnicodeScalar(32) : ucScalar }
.reduce("") { [=11=] + String() }
}
}
根据您对此 post 的评论(列出这些间隔),现有表情符号间隔已延长,因此表情符号检查现在可能是详尽无遗的。
Swift 4:
extension String {
func stringByRemovingEmoji() -> String {
return String(self.filter { ![=10=].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))!)
}
}
表情符号被 Unicode 归类为符号。字符集通常用于搜索操作。所以我们将使用字符集 a 属性 即 symbols.
var emojiString = "Hey there , welcome"
emojiString = emojiString.components(separatedBy: CharacterSet.symbols).joined()
print(emojiString)
输出是
Hey there , welcome
现在观察emoji被一个白色替换space所以有两个白色space我们用下面的方式替换它
emojiString.replacingOccurrences(of: " ", with: " ")
上述方法将"two white space"的参数替换为:"single white space"
我发现上面给出的解决方案不适用于某些字符,例如️♂️和。
为了找到表情符号范围,我使用正则表达式将 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 脚本,用于将十六进制字符串解析为整数,然后找到范围。
convert-hex-to-decimal.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
获取所有表情符号比您想象的要复杂。有关如何确定哪些字符是表情符号的更多信息,请查看此 post or 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 {
[=10=].properties.isJoinControl ||
[=10=].properties.isVariationSelector
}
}
var isEmoji: Bool {
return isSimpleEmoji || isCombinedIntoEmoji
}
}
extension String {
func replaceEmoji(with character: Character) -> String {
return String(map { [=10=].isEmoji ? character : [=10=] })
}
}
使用它会变成:
"Some string with emoji".replaceEmoji(with: " ")
Swift 5
不要使用这种hardcoded
方式检测emojis
。在Swift 5
你可以轻松做到
let inputText = "Some string with emoji "
let textWithoutEmoij = inputText.unicodeScalars
.filter { ![=10=].properties.isEmojiPresentation }
.reduce("") { [=10=] + String() }
print(textWithoutEmoij) // Some string with emoji
不要hard-code表情符号的范围,改用这个。
func 去除表情符号(字符串:String) -> String {
let 转换为Unicode = 字符串.unicodeScalars//https://developer.apple.com/documentation/swift/string
let 去除表情后的结果 = 转换为Unicode.filter { (item) -> Bool in
let 判断是否表情 = item.properties.isEmoji
return !判断是否表情//是表情就不保留
}
return String(去除表情后的结果)
}
我有一种方法可以检测字符串中的 urls 并且 returns 我可以检测到 urls 和可以找到它们的范围。一切正常,直到字符串上出现表情符号。例如:
"I'm gonna do this callenge as soon as I can swing again \n http://youtu.be/SW_d3fGz1hk"
由于表情符号,从文本中提取的 url 是 http://youtu.be/SW_d3fGz1 instead of http://youtu.be/SW_d3fGz1hk。我认为最简单的解决方案是将字符串中的表情符号替换为空白字符(因为我需要正确的范围来处理某些文本样式)。问题是,使用 Swift 很难做到这一点(很可能我缺乏使用 Swift 字符串 API 的能力)。
我一直在尝试这样做,但似乎无法从 unicode 点数组创建字符串:
var emojilessStringWithSubstitution: String {
let emojiRanges = [0x1F601...0x1F64F, 0x2702...0x27B0]
let emojiSet = Set(emojiRanges.flatten())
let codePoints: [UnicodeScalar] = self.unicodeScalars.map {
if emojiSet.contains(Int([=12=].value)) {
return UnicodeScalar(32)
}
return [=12=]
}
return String(codePoints)
}
我是不是以错误的方式解决了这个问题?在这里替换表情符号是最好的解决方案吗?如果可以,我该怎么做?
您可以使用模式匹配(针对表情符号模式)从您的 String
中过滤掉表情符号字符。
extension String {
var emojilessStringWithSubstitution: String {
let emojiPatterns = [UnicodeScalar(0x1F601)...UnicodeScalar(0x1F64F),
UnicodeScalar(0x2702)...UnicodeScalar(0x27B0)]
return self.unicodeScalars
.filter { ucScalar in !(emojiPatterns.contains{ [=10=] ~= ucScalar }) }
.reduce("") { [=10=] + String() }
}
}
/* 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 */
请注意,以上仅使用问题中提供的表情符号间隔,并不代表所有表情符号,但该方法是通用的,可以通过在 emojiPatterns
数组.
我意识到再次阅读你的问题,你更喜欢用空白字符替换表情符号,而不是删除它们(上面的过滤解决方案就是这样做的)。我们可以通过用条件 return .map
操作替换上面的 .filter
操作来实现这一点,就像您的问题
extension String {
var emojilessStringWithSubstitution: String {
let emojiPatterns = [UnicodeScalar(0x1F600)...UnicodeScalar(0x1F64F),
UnicodeScalar(0x1F300)...UnicodeScalar(0x1F5FF),
UnicodeScalar(0x1F680)...UnicodeScalar(0x1F6FF),
UnicodeScalar(0x2600)...UnicodeScalar(0x26FF),
UnicodeScalar(0x2700)...UnicodeScalar(0x27BF),
UnicodeScalar(0xFE00)...UnicodeScalar(0xFE0F)]
return self.unicodeScalars
.map { ucScalar in
emojiPatterns.contains{ [=11=] ~= ucScalar } ? UnicodeScalar(32) : ucScalar }
.reduce("") { [=11=] + String() }
}
}
根据您对此 post 的评论(列出这些间隔),现有表情符号间隔已延长,因此表情符号检查现在可能是详尽无遗的。
Swift 4:
extension String {
func stringByRemovingEmoji() -> String {
return String(self.filter { ![=10=].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))!)
}
}
表情符号被 Unicode 归类为符号。字符集通常用于搜索操作。所以我们将使用字符集 a 属性 即 symbols.
var emojiString = "Hey there , welcome"
emojiString = emojiString.components(separatedBy: CharacterSet.symbols).joined()
print(emojiString)
输出是
Hey there , welcome
现在观察emoji被一个白色替换space所以有两个白色space我们用下面的方式替换它
emojiString.replacingOccurrences(of: " ", with: " ")
上述方法将"two white space"的参数替换为:"single white space"
我发现上面给出的解决方案不适用于某些字符,例如️♂️和。
为了找到表情符号范围,我使用正则表达式将 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 脚本,用于将十六进制字符串解析为整数,然后找到范围。
convert-hex-to-decimal.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
获取所有表情符号比您想象的要复杂。有关如何确定哪些字符是表情符号的更多信息,请查看此
基于这些信息,我建议使用 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 {
[=10=].properties.isJoinControl ||
[=10=].properties.isVariationSelector
}
}
var isEmoji: Bool {
return isSimpleEmoji || isCombinedIntoEmoji
}
}
extension String {
func replaceEmoji(with character: Character) -> String {
return String(map { [=10=].isEmoji ? character : [=10=] })
}
}
使用它会变成:
"Some string with emoji".replaceEmoji(with: " ")
Swift 5
不要使用这种hardcoded
方式检测emojis
。在Swift 5
你可以轻松做到
let inputText = "Some string with emoji "
let textWithoutEmoij = inputText.unicodeScalars
.filter { ![=10=].properties.isEmojiPresentation }
.reduce("") { [=10=] + String() }
print(textWithoutEmoij) // Some string with emoji
不要hard-code表情符号的范围,改用这个。
func 去除表情符号(字符串:String) -> String {
let 转换为Unicode = 字符串.unicodeScalars//https://developer.apple.com/documentation/swift/string
let 去除表情后的结果 = 转换为Unicode.filter { (item) -> Bool in
let 判断是否表情 = item.properties.isEmoji
return !判断是否表情//是表情就不保留
}
return String(去除表情后的结果)
}