希腊字母的字符串范围不一致

Inconsistent ranges of strings with greek letter

我将展示代码和输出,因为它更容易解释问题。

注释行中的代码和输出:

let greekLetter = "β"

let string1 = greekLetter

/// string2 is the same as string1 but converted to NSString then back to String
let string2 = String(NSString(string: greekLetter))

print(string1.range(of: greekLetter)!)
/// prints: Index(_rawBits: 0)..<Index(_rawBits: 131072)

print(string2.range(of: greekLetter)!)
/// prints: Index(_rawBits: 0)..<Index(_rawBits: 65536)

问题:一个包含希腊字母的字符串 returns 一个范围不同于具有相同希腊字母的相同字符串,该字符串已转换为 NSString 然后再次返回到 String。

知道为什么吗?

为什么提出这个问题: 我正在做一些解析,我需要找到特定字符串的范围,然后插入其他内容来代替它。由于错误的范围返回错误的位置插入字符串由于错误 lower/upper 绑定位置。

更新 2:

假设我有一个任务:在给定的字符串“β-1”中将“1”更改为“2”。这个字符串来自服务器。 请查看此代码示例:


let wordWithGreekLetter = "β-1"

var string1 = wordWithGreekLetter

let data = """
    { "name" : "\(wordWithGreekLetter)" }
""".data(using: String.Encoding.utf8)

struct User: Decodable {
    let name: String
}

let user = try! JSONDecoder().decode(User.self, from: data!)

/// string2 is the same as string1 but decoded from the data
var string2 = user.name

let rangeOfNumberOne1 = string1.range(of: "1")!
string1.removeSubrange(rangeOfNumberOne1)
string1.insert("2", at: rangeOfNumberOne1.lowerBound)
/// RESULT: string1 = "β-2"

let rangeOfNumberOne2 = string2.range(of: "1")!
string2.removeSubrange(rangeOfNumberOne2)
string2.insert("2", at: rangeOfNumberOne2.lowerBound)
/// RESULT: string2 = "β2-"


正如 Rob 在 中解释的那样,索引的原始位是一个实现细节,您不应该关心该值。

实际问题是(引自Collection):

Saved indices may become invalid as a result of mutating operations.

这样 rangeOfNumberOne1/2 可能在您对字符串调用 removeSubrange() 后不再有效。

在这种特殊情况下,可能 发生在 string2(从 NSString 桥接),因为删除字符 可能 重组内部存储。但这纯粹是猜测:唯一重要的是当前代码表现出未定义的行为。

如果替换

let rangeOfNumberOne1 = string1.range(of: "1")!
string1.removeSubrange(rangeOfNumberOne1)
string1.insert("2", at: rangeOfNumberOne1.lowerBound)

来自

let rangeOfNumberOne1 = string1.range(of: "1")!
string1.replaceSubrange(rangeOfNumberOne1, with: "2")

(和 string2 类似)然后你会得到相同的结果 "β-2" 对于两个字符串。