与NSString API交互时应该使用哪个Swift字符计数?

时间:2018-07-20 06:35:00

标签: swift string

有时,我需要使用依赖于后台的NSString / NSRange的API,但是我的大部分代码都在Swift中。

当我需要提供索引(或范围)时,应该使用哪个Swift字符计数?

例如,给定此功能:

func replace(_ string: String, characterAtIndex characterIndex: Int) -> String {
  let regex = try! NSRegularExpression(pattern: ".", options: [])
  let range = NSRange(location: characterIndex, length: 1)
  let mutableString = NSMutableString(string: string)
  regex.replaceMatches(in: mutableString, options: [], range: range, withTemplate: "!")
  return mutableString as String
}

我应该使用6种不同的方式来获取字符串中的字符计数吗?

1 个答案:

答案 0 :(得分:0)

TL; DR

documentation for NSString.length指定:

  

接收器中UTF-16代码单元的数量。

因此,如果要在String和NSString之间互操作

  • 您应该使用string.utf16.count,它将与(string as NSString).length完美匹配。

如果要计算可见字符数

  • 您应该使用string.count,它将匹配您需要键盘上的(向右)键的相同次数,直到结束为止的字符串(假设您从头开始)。

    注意:这并不总是100%准确,但苹果似乎正在不断改进其实现,以使其越来越准确。


这里是一个Swift 4.0游乐场,用于测试一堆字符串和函数:

let header = "NSString   .utf16❔   encodedOffset❔   NSRange❔   .count❔   .characters❔   distance❔   .unicodeScalars❔   .utf8❔   Description"
var format = "     %3d     %3d ❓            %3d ❓      %3d ❓     %3d ❓          %3d ❓       %3d ❓              %3d ❓    %3d ❓   %@"
format = format.replacingOccurrences(of: "❓", with: "%@") // "❓" acts as a placeholder for "%@" to align the text perfectly

print(header)

test("")
test("abc")
test("❌")
test("")
test("☾test")
test("‍‍‍")
test("\u{200d}\u{200d}\u{200d}")
test("")
test("\u{1F468}")
test("‍♀️‍♂️")
test("你好吗")
test("مرحبا", "Arabic word")
test("م", "Arabic letter")
test("שלום", "Hebrew word")
test("ם", "Hebrew letter")

func test(_ s: String, _ description: String? = nil) {
  func icon(for length: Int) -> String {
    return length == (s as NSString).length ? "✅" : "❌"
  }

  let description = description ?? "'" + s + "'"
  let string = String(
    format: format,
    (s as NSString).length,
    s.utf16.count, icon(for: s.utf16.count),
    s.endIndex.encodedOffset, icon(for: s.endIndex.encodedOffset),
    NSRange(s.startIndex..<s.endIndex, in: s).upperBound, icon(for: NSRange(s.startIndex..<s.endIndex, in: s).upperBound),
    s.count, icon(for: s.count),
    s.characters.count, icon(for: s.characters.count),
    s.distance(from: s.startIndex, to: s.endIndex), icon(for: s.distance(from: s.startIndex, to: s.endIndex)),
    s.unicodeScalars.count, icon(for: s.unicodeScalars.count),
    s.utf8.count, icon(for: s.utf8.count),
    description)
  print(string)
}

这是输出:

NSString   .utf16❔   encodedOffset❔   NSRange❔   .count❔   .characters❔   distance❔   .unicodeScalars❔   .utf8❔   Description
       0       0 ✅              0 ✅        0 ✅       0 ✅            0 ✅         0 ✅                0 ✅      0 ✅   ''
       3       3 ✅              3 ✅        3 ✅       3 ✅            3 ✅         3 ✅                3 ✅      3 ✅   'abc'
       1       1 ✅              1 ✅        1 ✅       1 ✅            1 ✅         1 ✅                1 ✅      3 ❌   '❌'
       4       4 ✅              4 ✅        4 ✅       1 ❌            1 ❌         1 ❌                2 ❌      8 ❌   ''
       5       5 ✅              5 ✅        5 ✅       5 ✅            5 ✅         5 ✅                5 ✅      7 ❌   '☾test'
      11      11 ✅             11 ✅       11 ✅       1 ❌            1 ❌         1 ❌                7 ❌     25 ❌   '‍‍‍'
      11      11 ✅             11 ✅       11 ✅       1 ❌            1 ❌         1 ❌                7 ❌     25 ❌   '‍‍‍'
       8       8 ✅              8 ✅        8 ✅       4 ❌            4 ❌         4 ❌                4 ❌     16 ❌   ''
       2       2 ✅              2 ✅        2 ✅       1 ❌            1 ❌         1 ❌                1 ❌      4 ❌   ''
      58      58 ✅             58 ✅       58 ✅      13 ❌           13 ❌        13 ❌               32 ❌    122 ❌   '‍♀️‍♂️'
       3       3 ✅              3 ✅        3 ✅       3 ✅            3 ✅         3 ✅                3 ✅      9 ❌   '你好吗'
       5       5 ✅              5 ✅        5 ✅       5 ✅            5 ✅         5 ✅                5 ✅     10 ❌   Arabic word
       1       1 ✅              1 ✅        1 ✅       1 ✅            1 ✅         1 ✅                1 ✅      2 ❌   Arabic letter
       4       4 ✅              4 ✅        4 ✅       4 ✅            4 ✅         4 ✅                4 ✅      8 ❌   Hebrew word
       1       1 ✅              1 ✅        1 ✅       1 ✅            1 ✅         1 ✅                1 ✅      2 ❌   Hebrew letter

结论:

  • 要获得与NSString / NSRange兼容的长度,请使用(s as NSString).lengths.utf16.count(首选),s.endIndex.encodedOffsetNSRange(s.startIndex..<s.endIndex, in: s)
  • 要获取可见字符的数量,请使用s.count(首选),s.characters.count(不推荐使用)或s.distance(from: s.startIndex, to: s.endIndex)

一个有用的扩展,可以获取完整的字符串:

public extension String {

  var nsrange: NSRange {
    return NSRange(startIndex..<endIndex, in: self)
  }
}

因此,您可以像这样调用原始方法:

replace("‍‍‍", characterAtIndex: "‍‍‍".utf16.count - 1) // ‍‍‍�!