【问题标题】:In Swift, how to get estimate of String length in constant time?在 Swift 中,如何在恒定时间内估计字符串长度?
【发布时间】:2017-09-07 18:47:03
【问题描述】:

在 Swift 3 中,您可以计算 String 中的字符数:

str.characters.count

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

str.utf16.count

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

【问题讨论】:

  • 您是否进行了性能测试,发现计算字符串长度是您的瓶颈?每次用户输入字符时,您都可以检查字符串的长度。仅仅因为某些东西是 O(n) 并不意味着它很慢。
  • 你可以试试star.characters.underestimatedCount。但是您可能看不到标准 count 属性的显着性能提升。对于符合 RandomAccessCollection 协议的集合,这两个属性在 O(1) 时间内计算,而 String 不这样做。

标签: swift string swift3


【解决方案1】:

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

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,它可以包装一个 Foundation 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,它是在Objective-C 中通过NSAttributedString 实现的。如果您愿意承担 Objective-C 消息传递开销(老实说,这可能会在场景下发生以生成 String),您可以调用它的 length 属性,因为NSAttributedString 建立在 NSString 之上,确实保证它在内部使用 UTF-16,几乎可以肯定是恒定时间。

【讨论】:

  • 我可能是错的,但我记得看到一个帖子说 string.utf16.countO(n) stackoverflow.com/questions/33094432/…
  • 那是两年前的事了,当时还没有源代码。从那时起,情况也可能发生了变化。源代码似乎暗示它是O(1),除非我读错了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-18
  • 2013-04-27
  • 1970-01-01
  • 1970-01-01
  • 2013-09-19
  • 2011-10-30
  • 1970-01-01
相关资源
最近更新 更多