2016-10-25 3 views
0

원시 HTTP 응답을 구문 분석하려고하는데 NSRange를 범위로 변환하려고하면 잘못된 범위가 나타납니다. 여기에 놀이터에서 관련 코드 :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) 

왜 대신 18에서 시작하는 문자열의 범위를 반환하는 3 toStringRange() 호출 17의?

답변

1

NSRange에서 Range<String.Index>로 전환 방법은 "기본 다국어 평면"(그림 이모티콘, 플래그 등)의 외부 확장 그래 핀 클러스터와 문자를하지 작업을 제대로 수행합니다. (NSStringunichar 표현에 대응)

NSRange 카운트 UTF-16 코드 포인트. Range<String.Index> 카운트 Swift Characters 확장 된 축약 형 클러스터를 나타냅니다.

당신의 구체적인 경우

,이 UTF-16 코드 포인트로 "\r\n" 수,하지만 하나의 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" 
} 
을 여기

은 간단한 예입니다
관련 문제