如何在混合的Swift String / NSString环境中使用范围

咱们仍然在谈论Swift字符串的主题,今天咱们将会看到你在使用String和处理时遇到的问题NSString。若是您使用来自AppKit和Foundation的文本API,则必须处理这两种字符串类型,而且须要注意一些棘手的差别。swift

咱们将使用一个小示例应用程序来播放字符串中的字符串和范围。咱们要看的第一件事是Swift字符串和NSStrings的范围之间的差别。安全

将范围转换为NSRange

当咱们输入搜索字段时,咱们想在文本视图中突出显示找到的单词。视图控制器已经有一个方法,search咱们能够在其中使用text为搜索字段的文本提供的属性Stringbash

func search(_ searchTerm: String) {
   guard let range = text.range(of: searchTerm) else { return }

}
复制代码

在Swift字符串中找到范围后,咱们但愿使用方法showFindIndicator(for charRange: NSRange)on 在文本视图中突出显示此范围NSTextView。不幸的是,咱们不能直接将咱们传递Range给这个方法 - 咱们应该先用新的NSRange初始化器转换它:ui

func search(_ searchTerm: String) {
    guard let range = text.range(of: searchTerm) else { return }
    let nsRange = NSRange(range, in: text)
    textView.showFindIndicator(for: nsRange)
}
复制代码

咱们运行应用程序并看到它的工做原理:在搜索字段中输入“Hello”后,文本视图中的“Hello”字样闪烁。若是输入“World”,它也会这样作。spa

这是一个简单的解决方案,但若是您不使用正确的NSRange初始化程序,则很容易出错。咱们在Stack Overflow上看到了不少代码,在其余地方人们手动计算索引来构造NSRange3d

func search(_ searchTerm: String) {
    guard let range = text.range(of: searchTerm) else { return }
    let location = text.distance(from: text.startIndex, to: range.lowerBound)
    let length = text.distance(from: range.lowerBound, to: range.upperBound)
    let nsRange = NSRange(location: location, length: length)
    textView.showFindIndicator(for: nsRange)
}
复制代码

04:09这段代码编译,可是错了。咱们一开始可能不会注意到,由于若是咱们搜索“你好”,正确的单词会闪烁,但若是咱们搜索“世界”,第一个表情符号会突出显示!code

Swift弦乐和NSStrings对角色的概念有不一样的概念; 咱们不能经过简单地计算索引Range来转换为a NSRange,由于它们具备不一样的索引类型。具备肤色修饰符的女性消防员表情符号可能看起来像单个字符,但它不适用于NSString。cdn

咱们恢复正确的代码并继续咱们的应用程序的第二个功能。blog

将NSRange转换为Range

当咱们选择文本的一部分时,咱们但愿在右侧的信息面板中显示有关该选择的一些信息。此次,咱们必须将NSRange文本视图转换为a Range,以便从Swift字符串中获取子字符串text并将其传递给名为的标签selection索引

func updateInfo() {
    let nsRange = textView.selectedRange()
    let range = Range(nsRange, in: text)!
    let value = String(text[range])
    selection.stringValue = value
}
复制代码

咱们能够运行应用程序并检查正确的文本是否显示为选择。一样,使用正确的范围转换初始化器很重要,由于不然咱们会遇到与之前相同的问题,但此次是在另外一个方向:从。NSRangeRange

比较字符数

既然咱们有正确的字符串,咱们可使用有关选择的信息填充其余标签:

func updateInfo() {
    let nsRange = textView.selectedRange()
    let range = Range(nsRange, in: text)!
    let value = String(text[range])
    selection.stringValue = value
    characterCount.stringValue = String(value.count)
    nsStringCount.stringValue = String((value as NSString).length)
    utf16Count.stringValue = String(value.utf16.count)
    unicodeScalarCount.stringValue = String(value.unicodeScalars.count)
    byteCount.stringValue = String(value.data(using: .utf8)!.count)
}
复制代码

这会产生一些有趣的信息。若是咱们再次选择“Hello”,则信息看起来很简单,由于Swift字符串和NSString都是五个字符长,Swift字符串的其余视图也是如此:

若是咱们选择👩🏻🚒表情符号,事情会变得更加复杂。正如预期的那样,Swift字符串是一个字符长。NSString表示它的长度为7 - 这与Swift字符串utf16视图的长度匹配,由于NSStrings由UTF-16支持:

Swift字符串的unicodeScalars视图返回四个计数。您能够在线查看Unicode字符的元素。在这种状况下,消防员表情符号字符由“女人”,肤色修饰符,零宽度木匠和“消防引擎”组成 - 总共有四个标量。

法国国旗表情符号🇫🇷被列为一个Swift字符串字符,四个NSString字符和两个Unicode标量(特殊标志版本的“F”和“R”)。考虑全部这些不一样的计数,混合范围和指数是行不通的。若是你只处理简单的字母字符,你可使用RangeNSRange互换。可是只要你遇到一个更复杂的角色,好比表情符号,如下索引在各类字符串视图中都会有所不一样。

家庭角色也颇有趣。咱们使用的表情符号👨👩👧由三个标尺组成,用于我的,另外还有两个零宽度链接器将它们组合成一个系列:总共五个标量。

结论

在对字符串的原始数据进行操做时,咱们必需要当心,由于咱们可能会更改单个系列成员的字节并没有意中更改整个系列字符。对整个字符进行操做比对单个字节或标量进行操做更安全。幸运的是,咱们今天看到的两个初始化器,转换RangeNSRange和返回,使事情变得更容易。


转载自:www.jianshu.com/p/d30cce510…

相关文章
相关标签/搜索