是否可以编写一个 Swift 函数来仅替换像 这样的扩展字素簇的一部分?
Is it possible to write a Swift function that replaces only part of an extended grapheme cluster like ?
我想写一个可以像这样使用的函数:
let = "".replacingFirstOccurrence(of: "", with: "")
鉴于 ,这在 Swift 中可能吗?
根据在 获得的见解,一个明智的方法可能是替换 Unicode 标量:
extension String {
func replacingFirstOccurrence(of target: UnicodeScalar, with replacement: UnicodeScalar) -> String {
let uc = self.unicodeScalars
guard let idx = uc.index(of: target) else { return self }
let prefix = uc[uc.startIndex..<idx]
let suffix = uc[uc.index(after: idx) ..< uc.endIndex]
return "\(prefix)\(replacement)\(suffix)"
}
}
示例:
let family1 = ""
print(family1.characters.map { Array(String([=11=]).unicodeScalars) })
// [["\u{0001F469}", "\u{200D}"], ["\u{0001F469}", "\u{200D}"], ["\u{0001F467}", "\u{200D}"], ["\u{0001F466}"]]
let family2 = family1.replacingFirstOccurrence(of: "", with: "")
print(family2) //
print(family2.characters.map { Array(String([=11=]).unicodeScalars) })
// [["\u{0001F469}", "\u{200D}"], ["\u{0001F469}", "\u{200D}"], ["\u{0001F466}", "\u{200D}"], ["\u{0001F466}"]]
这是一个可能的版本,它定位并替换任意字符串的 Unicode 标量:
extension String {
func replacingFirstOccurrence(of target: String, with replacement: String) -> String {
let uc = self.unicodeScalars
let tuc = target.unicodeScalars
// Target empty or too long:
if tuc.count == 0 || tuc.count > uc.count {
return self
}
// Current search position:
var pos = uc.startIndex
// Last possible position of `tuc` within `uc`:
let end = uc.index(uc.endIndex, offsetBy: tuc.count - 1)
// Locate first Unicode scalar
while let from = uc[pos..<end].index(of: tuc.first!) {
// Compare all Unicode scalars:
let to = uc.index(from, offsetBy: tuc.count)
if !zip(uc[from..<to], tuc).contains(where: { [=12=] != }) {
let prefix = uc[uc.startIndex..<from]
let suffix = uc[to ..< uc.endIndex]
return "\(prefix)\(replacement)\(suffix)"
}
// Next search position:
uc.formIndex(after: &pos)
}
// Target not found.
return self
}
}
使用 range(of:options:range:locale:)
解决方案变得非常简洁:
extension String {
func replaceFirstOccurrence(of searchString: String, with replacementString: String) -> String {
guard let range = self.range(of: searchString, options: .literal) else { return self }
return self.replacingCharacters(in: range, with: replacementString)
}
}
首先在实例中找到 searchString
的范围,如果找到范围,则将范围替换为 replacementString
。否则实例只是 returns 本身。而且,由于 range(of:)
方法 returns 一旦找到匹配项,返回的范围保证是第一次出现。
"221".replaceFirstOccurrence(of: "2", with: "3") // 321
"".replaceFirstOccurrence(of: "\u{1f469}", with: "\u{1f468}") //
*澄清一下,最后一个测试用例将 woman-woman-girl-boy 转换为 man-woman-girl-boy.
我想写一个可以像这样使用的函数:
let = "".replacingFirstOccurrence(of: "", with: "")
鉴于
根据在
extension String {
func replacingFirstOccurrence(of target: UnicodeScalar, with replacement: UnicodeScalar) -> String {
let uc = self.unicodeScalars
guard let idx = uc.index(of: target) else { return self }
let prefix = uc[uc.startIndex..<idx]
let suffix = uc[uc.index(after: idx) ..< uc.endIndex]
return "\(prefix)\(replacement)\(suffix)"
}
}
示例:
let family1 = ""
print(family1.characters.map { Array(String([=11=]).unicodeScalars) })
// [["\u{0001F469}", "\u{200D}"], ["\u{0001F469}", "\u{200D}"], ["\u{0001F467}", "\u{200D}"], ["\u{0001F466}"]]
let family2 = family1.replacingFirstOccurrence(of: "", with: "")
print(family2) //
print(family2.characters.map { Array(String([=11=]).unicodeScalars) })
// [["\u{0001F469}", "\u{200D}"], ["\u{0001F469}", "\u{200D}"], ["\u{0001F466}", "\u{200D}"], ["\u{0001F466}"]]
这是一个可能的版本,它定位并替换任意字符串的 Unicode 标量:
extension String {
func replacingFirstOccurrence(of target: String, with replacement: String) -> String {
let uc = self.unicodeScalars
let tuc = target.unicodeScalars
// Target empty or too long:
if tuc.count == 0 || tuc.count > uc.count {
return self
}
// Current search position:
var pos = uc.startIndex
// Last possible position of `tuc` within `uc`:
let end = uc.index(uc.endIndex, offsetBy: tuc.count - 1)
// Locate first Unicode scalar
while let from = uc[pos..<end].index(of: tuc.first!) {
// Compare all Unicode scalars:
let to = uc.index(from, offsetBy: tuc.count)
if !zip(uc[from..<to], tuc).contains(where: { [=12=] != }) {
let prefix = uc[uc.startIndex..<from]
let suffix = uc[to ..< uc.endIndex]
return "\(prefix)\(replacement)\(suffix)"
}
// Next search position:
uc.formIndex(after: &pos)
}
// Target not found.
return self
}
}
使用 range(of:options:range:locale:)
解决方案变得非常简洁:
extension String {
func replaceFirstOccurrence(of searchString: String, with replacementString: String) -> String {
guard let range = self.range(of: searchString, options: .literal) else { return self }
return self.replacingCharacters(in: range, with: replacementString)
}
}
首先在实例中找到 searchString
的范围,如果找到范围,则将范围替换为 replacementString
。否则实例只是 returns 本身。而且,由于 range(of:)
方法 returns 一旦找到匹配项,返回的范围保证是第一次出现。
"221".replaceFirstOccurrence(of: "2", with: "3") // 321
"".replaceFirstOccurrence(of: "\u{1f469}", with: "\u{1f468}") //
*澄清一下,最后一个测试用例将 woman-woman-girl-boy 转换为 man-woman-girl-boy.