在Swift中,如何在恒定时间内估算字符串长度?

时间:2017-09-07 18:47:04

标签: swift string swift3

在Swift 3中,您可以使用{<1}}计算字符:

String

我需要经常这样做,上面那行看起来可能是O(N)。有没有办法获得一个字符串长度,或某些的长度 - 可能是底层的unicode缓冲区 - 一个操作,保证不必走遍整个字符串?也许:

str.characters.count

我问,因为我每次用户输入一个字符时都会查看某些文字的长度,以限制str.utf16.count 的大小。调用不需要是字形的精确计数,例如UITextView

1 个答案:

答案 0 :(得分:4)

这是一个很好的问题。答案是......复杂的。从UTF-8转换为UTF-16,反之亦然,或转换为或从其他编码转换,都需要检查字符串,因为字符可以由多个代码单元组成。因此,如果您希望在不变的时间内获得计数,那么它将归结为内部表示的内容。如果字符串在内部使用UTF-16,那么它是一个合理的假设string.utf16.count将处于恒定时间,但如果内部表示是UTF-8或其他东西,那么字符串将需要是分析以确定UTF-16的长度。那么内部使用的字符串是什么?良好:

https://github.com/apple/swift/blob/master/stdlib/public/core/StringCore.swift

/// The core implementation of a highly-optimizable String that
/// can store both ASCII and UTF-16, and can wrap native Swift
/// _StringBuffer or NSString instances.

这令人沮丧。内部表示可以是ASCII UTF-16,它可以包装基金会NSString。人力资源管理。我们知道NSString在内部使用UTF-16,因为这是actually documented,所以这很好。所以这里的主要异常值是字符串存储ASCII时。节省的优点是,由于前128个Unicode代码点与ASCII字符集具有相同的值,因此任何ASCII字符0xXX都应对应于UTF-16字符0x00XX,因此UTF-16长度应该只是ASCII长度乘以2,因此可以在恒定时间内计算。这是实施中的情况吗?我们来看看。

the UTF16View source中,没有count的实施。看来count继承自Collection's implementation,通过distance()实施:

public var count: IndexDistance {
  return distance(from: startIndex, to: endIndex)
}

UTF16View's implementation of distance()看起来像这样:

public func distance(from start: Index, to end: Index) -> IndexDistance {
  // FIXME: swift-3-indexing-model: range check start and end?
  return start.encodedOffset.distance(to: end.encodedOffset)
}

the String.Index source中,encodedOffset看起来像这样:

public var encodedOffset : Int {
  return Int(_compoundOffset >> _Self._strideBits)
}

其中_compoundOffset似乎是一个简单的64位整数:

internal var _compoundOffset : UInt64

_strideBits似乎也是一个直的整数:

internal static var _strideBits : Int { return 2 }

所以......看起来......就像你应该从string.utf16.count获得恒定的时间,因为除非我在某个地方犯了错误,否则你只需要移位几个整数然后比较结果(我可能仍然会进行一些测试以确定)。当然,需要注意的是,这没有记录,因此可能会在未来发生变化 - 特别是因为documentation for String确实声称它需要遍历字符串:

  

与isEmpty不同,计算视图的count属性需要遍历字符串的元素。

尽管如此,您还是使用了UITextView,这是通过NSAttributedString在Objective-C中实现的。如果您愿意承担Objective-C消息传递开销(诚实地说,可能是在场景下发生的,以生成String),您可以调用它length属性,NSAttributedString构建于NSString之上, 保证内部使用UTF-16,几乎可以肯定是恒定的时间。