Swift 字符串可以包含NUL 字节(例如"Hello\u{0000}world!" 是有效的String),因此假设您的字符串以NUL 字节结尾,那么您的方法都不够。
相反,您可能希望采用@Larme 作为评论发布的方法:首先拆分数据,然后从这些切片创建字符串。
如果你的分隔符确实是一个NUL 字节,这可以很简单
import Foundation
func decode(_ data: Data, separator: UInt8) -> [String] {
data.split(separator: separator).map { String(decoding: $0, as: UTF8.self) }
}
let data = Data("Hello, world!\u{00}Following string.\u{00}And another one!".utf8)
print(decode(data, separator: 0x00))
// => ["Hello, world!", "Following string.", "And another one!"]
这里的split(separator:) 方法是Sequence.split(separator:maxSplits:omittingEmptySubsequences:),它采用单个Sequence.Element 的分隔符——在本例中为单个UInt8。 omittingEmptySubsequences 默认为 true,所以
- 如果空字符串是有效输入并且您需要处理它们,请确保传入
false。否则,
- 如果您的分隔符是
N NUL 连续字节,则此方法仍然适用:您将得到 N - 1 空拆分,所有拆分都将被丢弃
或者,如果您不想急切地预先分割整个缓冲区(例如,您可能正在寻找一个指示停止处理的标记值),您可以通过循环遍历缓冲区来分段分割缓冲区,然后使用Data.prefix(while:)获取由分隔符终止的前缀:
import Foundation
func process(_ data: Data, separator: UInt8, using action: (String) -> Bool) {
var slice = data[...]
while !slice.isEmpty {
let substring = String(decoding: slice.prefix(while: { $0 != separator }), as: UTF8.self)
if !action(substring) {
break
}
slice = slice.dropFirst(substring.utf8.count + 1)
}
}
let data = Data("Hello, world!\u{00}Following string.\u{00}And another one!".utf8)
process(data, separator: 0x00) { string in
print(string)
return true // continue
}
如果您的分隔符更复杂(例如,多个不同的字符长),您仍然可以使用Data 方法来查找您的分隔符序列的实例并自己拆分它们:
import Foundation
func decode(_ data: Data, separator: String) -> [String] {
// `firstRange(of:)` below takes a type conforming to `DataProtocol`.
// `String.UTF8View` doesn't conform, but `Array` does. This copy should
// be cheap if the separator is small.
let separatorBytes = Array(separator.utf8)
var strings = [String]()
// Slicing the data will give cheap no-copy views into it.
// This first slice is the full data blob.
var slice = data[...]
// As long as there's an instance of `separator` in the data...
while let separatorRange = slice.firstRange(of: separatorBytes) {
// ... pull out all of the bytes before it into a String...
strings.append(String(decoding: slice[..<separatorRange.lowerBound], as: UTF8.self))
// ... and skip past the separator to keep looking for more.
slice = slice[separatorRange.upperBound...]
}
// If there are no separators, in the string, or the last string is not
// terminated with a separator itself, pull out the remaining contents.
if !slice.isEmpty {
strings.append(String(decoding: slice, as: UTF8.self))
}
return strings
}
let separator = "\u{00}\u{20}\u{00}"
let data = Data("Hello, world!\(separator)Following string.\(separator)And another one!".utf8)
print(decode(data, separator: separator))
// => ["Hello, world!", "Following string.", "And another one!"]