2016-10-25 5 views
0

NSRangeをRangeに変換しようとすると、生のHTTPレスポンスを解析しようとしていますが、範囲が正しくありません。NSRegularExpressionを使用した奇妙な文字列範囲の動作

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) 

なぜ3 toStringRange()呼び出しではなく、18で始まる文字列の範囲を返してある:ここで遊び場から関連するコードであります17の? NSRangeからRange<String.Index>

答えて

1

あなたの変換方法は「基本多言語面」(絵文字、旗、など)の 外拡張書記素クラスタと文字の正しくない 作業を行います。

NSRangeは、UTF-16コードポイント(unichar の表現NSStringに対応)をカウントします。 Range<String.Index>カウントSwift Characters拡張された書記素クラスタを表します。

具体的なケースでは、"\r\n"は2つの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-" 
} 

あなたが期待する結果を得るでしょうNSRange to Range<String.Index>から方法

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 
    } 
} 

を使用して::

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

は簡略化した例であります
関連する問題