繁体   English   中英

使用NSRegularExpression匹配的奇怪字符串范围行为

[英]strange string range behavior using NSRegularExpression matches

我正在尝试解析原始HTTP响应,并且在尝试将NSRange转换为Range时遇到错误的范围。 以下是操场上的相关代码:

public extension NSRange {
    public func toStringRange(_ str: String) -> Range<String.Index>? {
        guard str.characters.count >= length - location  && location < str.characters.count else { return nil }
        let fromIdx = str.characters.index(str.startIndex, offsetBy: self.location)
        print("from: \(self.location) = \(fromIdx)")
        let toIdx = str.characters.index(fromIdx, offsetBy: self.length)
        return fromIdx..<toIdx
    }
}

let responseString = "HTTP/1.0 200 OK\r\nContent-Length: 193\r\nContent-Type: application/json\r\n"
let responseRange = NSRange(location: 0, length: responseString.characters.count)
let responseRegex = try! NSRegularExpression(pattern: "^(HTTP/1.\\d) (\\d+) (.*?\r\n)(.*)", options: [.anchorsMatchLines])
guard let matchResult = responseRegex.firstMatch(in: responseString, options: [], range: responseRange),
    matchResult.numberOfRanges == 5,
    let versionRange = matchResult.rangeAt(1).toStringRange(responseString),
    let statusRange = matchResult.rangeAt(2).toStringRange(responseString),
    let headersRange = matchResult.rangeAt(4).toStringRange(responseString)
    else { fatalError() }

toStringRange()中的打印输出为

from: 0 = Index(_base: Swift.String.UnicodeScalarView.Index(_position: 0), _countUTF16: 1)
from: 9 = Index(_base: Swift.String.UnicodeScalarView.Index(_position: 9), _countUTF16: 1)
from: 17 = Index(_base: Swift.String.UnicodeScalarView.Index(_position: 18), _countUTF16: 1)

为什么第三个toStringRange()调用返回的字符串范围从18开始而不是17?

NSRangeRange<String.Index>转换方法不适用于扩展的字素簇和“基本多语言平面”之外的字符(表情符号,标志等)。

NSRange计数UTF-16代码点(对应于NSStringunichar表示形式)。 Range<String.Index>计算代表扩展Characters簇的Swift Characters

在您的具体情况下, "\\r\\n"视为两个UTF-16代码点,但被视为单个Character ,这会导致不必要的“移位”。

这是一个简化的示例:

let responseString = "OK\r\nContent-Length"

let nsRange = (responseString as NSString).range(of: "Content")
print(nsRange.location, nsRange.length) // 4 7

if let sRange1 = nsRange.toStringRange(responseString) {
    print(responseString.substring(with: sRange1)) // "ontent-"
}

使用方法

extension String {
    func range(from nsRange: NSRange) -> Range<String.Index>? {
        guard
            let from16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location, limitedBy: utf16.endIndex),
            let to16 = utf16.index(from16, offsetBy: nsRange.length, limitedBy: utf16.endIndex),
            let from = String.Index(from16, within: self),
            let to = String.Index(to16, within: self)
            else { return nil }
        return from ..< to
    }
}

NSRange到Range <String.Index>,您将获得预期的结果:

if let sRange2 = responseString.range(from: nsRange) {
    print(responseString.substring(with: sRange2)) // "Content"
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM