有什么合理的方法可以访问 CharacterSet 的内容吗?
Is there any reasonable way to access the contents of a CharacterSet?
对于随机字符串生成器,我认为使用 CharacterSet
作为要使用的字母表的输入类型会很好,因为 CharacterSet.lowercaseLetters
等预定义集显然很有用(即使它们可能包含比您预期的更多样的字符集)。
但是,显然您只能查询字符集的成员资格,而不能枚举它们,更不用说索引它们了。我们得到的只是 _.bitmapRepresentation
,一个 8kb 的数据块,每个 (?) 字符都有一个指示位。但是,即使您通过索引 i
剥离单个位(这不太好,通过面向字节的 Data
),Character(UnicodeScalar(i))
也不会给出正确的字母。这意味着格式有些模糊——当然,它是 not documented.
当然我们可以 iterate over all characters (per plane) 但从成本角度来看,这是一个坏主意:20 个字符的集合可能需要迭代数万个字符。用 CS 术语来说:位向量是稀疏集的(非常)糟糕的实现。为什么他们选择在这里进行这种权衡,我不知道。
我是不是遗漏了什么,或者 CharacterSet
只是 Foundation
API 中的另一个死胡同?
根据您的定义,不,没有 "reasonable" 方式。这就是 NSCharacterSet 存储它的方式。它针对测试成员资格进行了优化,而不是枚举所有成员。
你的循环可以在代码点上增加一个计数器,或者它可以移动位(每个代码点一个),但无论哪种方式你都必须循环和测试。我的 Mac 上最高的 "Ll" 字符是 U+1D7CB (#120,779),所以如果你想在运行时计算这个字符列表,你的代码将必须至少循环那么多次。有关如何组织位向量的详细信息,请参阅 Objective-C version of the documentation。
好消息是速度很快。在我 10 岁的 Mac 上使用未经优化的代码,找到所有 1,841 lowercaseLetters
只需不到 1/10 秒。如果这仍然不够快,可以通过在启动时在后台执行一次来轻松隐藏成本。
bitmapRepresentation 已记录。
https://developer.apple.com/documentation/foundation/nscharacterset/1417719-bitmaprepresentation
因此像下面这样迭代该数据:
var offset = 0
for ( var i, w ) in CharacterSet.whitespaces.bitmapRepresentation.enumerated() {
if i % 8193 == 8192 {
offset += 1
continue
}
i -= offset
if w != 0 {
for j in 0 ..< 8 {
if w & ( 1 << j ) != 0 {
print( String( format:"%02X", i * 8 + j ) )
}
}
}
}
结果:
09
20
A0
1680
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
200A
200B
202F
205F
3000
在 the documentation 之后,这里是对 Satachito 的改进,通过实际考虑平面索引来支持非连续平面的情况:
extension CharacterSet {
func codePoints() -> [Int] {
var result: [Int] = []
var plane = 0
// following documentation at https://developer.apple.com/documentation/foundation/nscharacterset/1417719-bitmaprepresentation
for (i, w) in bitmapRepresentation.enumerated() {
let k = i % 8193
if k == 8192 {
// plane index byte
plane = Int(w) << 13
continue
}
let base = (plane + k) << 3
for j in 0 ..< 8 where w & 1 << j != 0 {
result.append(base + j)
}
}
return result
}
func printHexValues() {
codePoints().forEach { print(String(format:"%02X", [=10=])) }
}
}
用法
print("whitespaces:")
CharacterSet.whitespaces.printHexValues()
print()
print("two characters from different planes:")
CharacterSet(charactersIn: "").printHexValues()
结果
whitespaces:
09
20
A0
1680
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
200A
200B
202F
205F
3000
two characters from different planes:
1D6A8
CC791
演出
这实际上比遍历所有字符快 3 到 10 倍:与之前在 NSArray from NSCharacterset 的答案进行比较。
对于随机字符串生成器,我认为使用 CharacterSet
作为要使用的字母表的输入类型会很好,因为 CharacterSet.lowercaseLetters
等预定义集显然很有用(即使它们可能包含比您预期的更多样的字符集)。
但是,显然您只能查询字符集的成员资格,而不能枚举它们,更不用说索引它们了。我们得到的只是 _.bitmapRepresentation
,一个 8kb 的数据块,每个 (?) 字符都有一个指示位。但是,即使您通过索引 i
剥离单个位(这不太好,通过面向字节的 Data
),Character(UnicodeScalar(i))
也不会给出正确的字母。这意味着格式有些模糊——当然,它是 not documented.
当然我们可以 iterate over all characters (per plane) 但从成本角度来看,这是一个坏主意:20 个字符的集合可能需要迭代数万个字符。用 CS 术语来说:位向量是稀疏集的(非常)糟糕的实现。为什么他们选择在这里进行这种权衡,我不知道。
我是不是遗漏了什么,或者 CharacterSet
只是 Foundation
API 中的另一个死胡同?
根据您的定义,不,没有 "reasonable" 方式。这就是 NSCharacterSet 存储它的方式。它针对测试成员资格进行了优化,而不是枚举所有成员。
你的循环可以在代码点上增加一个计数器,或者它可以移动位(每个代码点一个),但无论哪种方式你都必须循环和测试。我的 Mac 上最高的 "Ll" 字符是 U+1D7CB (#120,779),所以如果你想在运行时计算这个字符列表,你的代码将必须至少循环那么多次。有关如何组织位向量的详细信息,请参阅 Objective-C version of the documentation。
好消息是速度很快。在我 10 岁的 Mac 上使用未经优化的代码,找到所有 1,841 lowercaseLetters
只需不到 1/10 秒。如果这仍然不够快,可以通过在启动时在后台执行一次来轻松隐藏成本。
bitmapRepresentation 已记录。
https://developer.apple.com/documentation/foundation/nscharacterset/1417719-bitmaprepresentation
因此像下面这样迭代该数据:
var offset = 0
for ( var i, w ) in CharacterSet.whitespaces.bitmapRepresentation.enumerated() {
if i % 8193 == 8192 {
offset += 1
continue
}
i -= offset
if w != 0 {
for j in 0 ..< 8 {
if w & ( 1 << j ) != 0 {
print( String( format:"%02X", i * 8 + j ) )
}
}
}
}
结果:
09
20
A0
1680
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
200A
200B
202F
205F
3000
在 the documentation 之后,这里是对 Satachito 的改进,通过实际考虑平面索引来支持非连续平面的情况:
extension CharacterSet {
func codePoints() -> [Int] {
var result: [Int] = []
var plane = 0
// following documentation at https://developer.apple.com/documentation/foundation/nscharacterset/1417719-bitmaprepresentation
for (i, w) in bitmapRepresentation.enumerated() {
let k = i % 8193
if k == 8192 {
// plane index byte
plane = Int(w) << 13
continue
}
let base = (plane + k) << 3
for j in 0 ..< 8 where w & 1 << j != 0 {
result.append(base + j)
}
}
return result
}
func printHexValues() {
codePoints().forEach { print(String(format:"%02X", [=10=])) }
}
}
用法
print("whitespaces:")
CharacterSet.whitespaces.printHexValues()
print()
print("two characters from different planes:")
CharacterSet(charactersIn: "").printHexValues()
结果
whitespaces:
09
20
A0
1680
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
200A
200B
202F
205F
3000
two characters from different planes:
1D6A8
CC791
演出
这实际上比遍历所有字符快 3 到 10 倍:与之前在 NSArray from NSCharacterset 的答案进行比较。