Swift에서 범위를 만드는 방법은 무엇입니까?


104

Objective-c에서는 NSRange를 사용하여 범위를 만듭니다.

NSRange range;

그렇다면 Swift에서 범위를 만드는 방법은 무엇입니까?


3
레인지가 필요한 것은 무엇입니까? Swift 문자열은를 사용 Range<String.Index>하지만 때로는 NSString및 작업이 필요 NSRange하므로 더 많은 컨텍스트가 유용합니다. – 그러나 stackoverflow.com/questions/24092884/…를 살펴보십시오 .
Martin R

답변:


258

Swift 4 업데이트

Swift 범위는보다 복잡 NSRange하고 Swift 3에서 더 쉽게 얻을 수 없습니다. 이러한 복잡성의 원인을 이해하려면 this and this를 읽으십시오 . 만드는 방법과 사용시기를 보여 드리겠습니다.

폐쇄 범위 : a...b

범위 연산자 는 유형에 대해 가능한 최대 값인 경우에도 element a element를 모두 포함하는 Swift 범위를 만듭니다 (예 :). 이 폐쇄 된 범위의 두 가지 유형은 다음과 같습니다 과bbInt.maxClosedRangeCountableClosedRange .

1. ClosedRange

Swift의 모든 범위의 요소는 비교 가능합니다 (즉, Comparable 프로토콜을 준수 함). 이를 통해 컬렉션에서 범위의 요소에 액세스 할 수 있습니다. 다음은 예입니다.

let myRange: ClosedRange = 1...3

let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]

그러나 a ClosedRange는 셀 수 없습니다 (즉, 시퀀스 프로토콜을 따르지 않음). 즉, for루프로 요소를 반복 할 수 없습니다 . 이를 위해서는 CountableClosedRange.

2. CountableClosedRange

이것은 범위를 반복 할 수 있다는 점을 제외하면 마지막 것과 유사합니다.

let myRange: CountableClosedRange = 1...3

let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]

for index in myRange {
    print(myArray[index])
}

반 개방 범위 : a..<b

이 범위 연산자는 element는 포함 a하지만 element 는 포함 하지 않습니다b . 이상과 같이,이 반 개방 범위의 두 가지 유형은 다음과 같습니다 RangeCountableRange.

1. Range

와 마찬가지로 ClosedRange, 당신은으로 된 컬렉션의 요소에 액세스 할 수 있습니다 Range. 예:

let myRange: Range = 1..<3

let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]

다시 말하지만, 당신은 Range 비교할 수만 있고 stridable이 아니기 때문에 .

2. CountableRange

A CountableRange는 반복을 허용합니다.

let myRange: CountableRange = 1..<3

let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]

for index in myRange {
    print(myArray[index])
}

NSRange

예를 들어 속성 문자열을NSRange 만들 때 Swift에서 때때로 사용할 수 있습니다 (반드시) , 그래서 만드는 방법을 아는 것이 도움이됩니다.

let myNSRange = NSRange(location: 3, length: 2)

이것은 시작 인덱스와 끝 인덱스가 아니라 위치와 길이 입니다. 여기의 예는 Swift 범위와 의미가 비슷합니다 3..<5. 그러나 유형이 다르기 때문에 서로 바꿔서 사용할 수 없습니다.

문자열이있는 범위

.....<범위 연산자는 범위를 생성하는 속기 방법입니다. 예를 들면 :

let myRange = 1..<3

동일한 범위를 만드는 긴 손 방법은

let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3

여기서 색인 유형이 Int. String그러나 문자열은 문자로 만들어지고 모든 문자가 같은 크기가 아니기 때문에 에서는 작동하지 않습니다 . (자세한 내용은 이 글 을 읽어 보세요.) 예를 들어 😀과 같은 이모티콘은 문자 "b"보다 더 많은 공간을 차지합니다.

NSRange 문제

NSRangeNSString이모티콘으로 실험 해보면 무슨 뜻인지 알 수 있습니다. 두통.

let myNSRange = NSRange(location: 1, length: 3)

let myNSString: NSString = "abcde"
myNSString.substring(with: myNSRange) // "bcd"

let myNSString2: NSString = "a😀cde"
myNSString2.substring(with: myNSRange) // "😀c"    Where is the "d"!?

웃는 얼굴은 저장하는 데 두 개의 UTF-16 코드 단위를 사용하므로 "d"를 포함하지 않는 예기치 않은 결과를 제공합니다.

신속한 솔루션

이 때문에 Swift Strings Range<String.Index>에서는 Range<Int>. 문자열 인덱스는 특정 문자열을 기반으로 계산되므로 이모티콘 또는 확장 자소 클러스터가 있는지 알 수 있습니다.

var myString = "abcde"
let start = myString.index(myString.startIndex, offsetBy: 1)
let end = myString.index(myString.startIndex, offsetBy: 4)
let myRange = start..<end
myString[myRange] // "bcd"

myString = "a😀cde"
let start2 = myString.index(myString.startIndex, offsetBy: 1)
let end2 = myString.index(myString.startIndex, offsetBy: 4)
let myRange2 = start2..<end2
myString[myRange2] // "😀cd"

범위를 하나는 양면 : a......b..<b

Swift 4에서는 상황이 약간 단순화되었습니다. 범위의 시작 또는 끝 지점을 유추 할 수있을 때마다 해제 할 수 있습니다.

Int

단측 정수 범위를 사용하여 컬렉션을 반복 할 수 있습니다. 다음은 설명서의 몇 가지 예입니다 .

// iterate from index 2 to the end of the array
for name in names[2...] {
    print(name)
}

// iterate from the beginning of the array to index 2
for name in names[...2] {
    print(name)
}

// iterate from the beginning of the array up to but not including index 2
for name in names[..<2] {
    print(name)
}

// the range from negative infinity to 5. You can't iterate forward
// over this because the starting point in unknown.
let range = ...5
range.contains(7)   // false
range.contains(4)   // true
range.contains(-1)  // true

// You can iterate over this but it will be an infinate loop 
// so you have to break out at some point.
let range = 5...

이것은 문자열 범위에서도 작동합니다. 한쪽 끝 str.startIndex또는 str.endIndex끝 으로 범위를 만드는 경우 해제 할 수 있습니다. 컴파일러는 그것을 추론합니다.

주어진

var str = "Hello, playground"
let index = str.index(str.startIndex, offsetBy: 5)

let myRange = ..<index    // Hello

다음을 사용하여 색인에서 str.endIndex로 이동할 수 있습니다. ...

var str = "Hello, playground"
let index = str.index(str.endIndex, offsetBy: -10)
let myRange = index...        // playground

또한보십시오:

노트

  • 다른 문자열에 하나의 문자열로 만든 범위를 사용할 수 없습니다.
  • 보시다시피 문자열 범위는 Swift에서 고통 스럽지만 이모 지 및 기타 유니 코드 스칼라를 더 잘 처리 할 수 ​​있습니다.

추가 연구


내 의견은 "NSRange 문제"하위 섹션과 관련이 있습니다. NSString내부적으로 문자를 UTF-16 인코딩으로 저장합니다. 전체 유니 코드 스칼라는 21 비트입니다. 웃는 얼굴 문자 ( U+1F600)는 단일 16 비트 코드 단위에 저장할 수 없으므로 16 비트 코드 단위를 NSRange기준으로 2 카운트에 분산 됩니다. 이 예에서 3 개의 코드 단위는 2 개의 문자 만 나타냅니다
Code Different

25

Xcode 8 베타 2 • Swift 3

let myString = "Hello World"
let myRange = myString.startIndex..<myString.index(myString.startIndex, offsetBy: 5)
let mySubString = myString.substring(with: myRange)   // Hello

Xcode 7 • Swift 2.0

let myString = "Hello World"
let myRange = Range<String.Index>(start: myString.startIndex, end: myString.startIndex.advancedBy(5))

let mySubString = myString.substringWithRange(myRange)   // Hello

또는 간단히

let myString = "Hello World"
let myRange = myString.startIndex..<myString.startIndex.advancedBy(5)
let mySubString = myString.substringWithRange(myRange)   // Hello


2

이렇게 사용

var start = str.startIndex // Start at the string's start index
var end = advance(str.startIndex, 5) // Take start index and advance 5 characters forward
var range: Range<String.Index> = Range<String.Index>(start: start,end: end)

let firstFiveDigit =  str.substringWithRange(range)

print(firstFiveDigit)

출력 : 안녕하세요


"Range"데이터 유형이 있습니다. 그렇다면 어떻게 사용할 수 있습니까?
vichhai

@vichhai 어디에서 사용하고 싶은지 자세히 설명해 주시겠습니까?
Dharmbir Singh

목표 c에서와 같이 : NSRange 범위;
vichhai

1

Swift 4에서도 Int를 사용하여 String 범위를 표현하는 간단한 기본 방법이 아직 없다는 것이 놀랍습니다. 범위별로 하위 문자열을 얻는 방법으로 Int를 제공 할 수있는 유일한 String 메서드는 prefixsuffix입니다.

문자열에 말할 때 NSRange처럼 말할 수 있도록 일부 변환 유틸리티를 보유하는 것이 유용합니다. 다음은 NSRange와 마찬가지로 위치와 길이를 취하고 다음을 반환하는 유틸리티입니다 Range<String.Index>.

func range(_ start:Int, _ length:Int) -> Range<String.Index> {
    let i = self.index(start >= 0 ? self.startIndex : self.endIndex,
        offsetBy: start)
    let j = self.index(i, offsetBy: length)
    return i..<j
}

예를 들어 "hello".range(0,1)"Range<String.Index>의 첫 번째 문자를 포용합니다 "hello". 보너스로, 나는 부정적인 위치를 허용 한 : "hello".range(-1,1)"는 IS Range<String.Index>의 마지막 문자를 수용 "hello".

Range<String.Index>Cocoa와 대화해야 할 때 (예를 들어 NSAttributedString 속성 범위를 다룰 때)를 NSRange 로 변환하는 것도 유용합니다 . Swift 4는이를위한 기본 방법을 제공합니다.

let nsrange = NSRange(range, in:s) // where s is the string

따라서 문자열 위치와 길이에서 NSRange로 직접 이동하는 다른 유틸리티를 작성할 수 있습니다.

extension String {
    func nsRange(_ start:Int, _ length:Int) -> NSRange {
        return NSRange(self.range(start,length), in:self)
    }
}

1

NSRange 개체를 만들려는 사람은 다음과 같이 만들 수 있습니다.

let range: NSRange = NSRange.init(location: 0, length: 5)

이것은 위치 0과 길이 5의 범위를 생성합니다


1
func replace(input: String, start: Int,lenght: Int, newChar: Character) -> String {
    var chars = Array(input.characters)

    for i in start...lenght {
        guard i < input.characters.count else{
            break
        }
        chars[i] = newChar
    }
    return String(chars)
}

0

다음 확장을 만들었습니다.

extension String {
    func substring(from from:Int, to:Int) -> String? {
        if from<to && from>=0 && to<self.characters.count {
            let rng = self.startIndex.advancedBy(from)..<self.startIndex.advancedBy(to)
            return self.substringWithRange(rng)
        } else {
            return nil
        }
    }
}

사용 예 :

print("abcde".substring(from: 1, to: 10)) //nil
print("abcde".substring(from: 2, to: 4))  //Optional("cd")
print("abcde".substring(from: 1, to: 0))  //nil
print("abcde".substring(from: 1, to: 1))  //nil
print("abcde".substring(from: -1, to: 1)) //nil

0

이렇게 사용할 수 있습니다

let nsRange = NSRange(location: someInt, length: someInt)

에서와 같이

let myNSString = bigTOTPCode as NSString //12345678
let firstDigit = myNSString.substringWithRange(NSRange(location: 0, length: 1)) //1
let secondDigit = myNSString.substringWithRange(NSRange(location: 1, length: 1)) //2
let thirdDigit = myNSString.substringWithRange(NSRange(location: 2, length: 4)) //3456

0

나는 이것을하고 싶다 :

print("Hello"[1...3])
// out: Error

하지만 안타깝게도 혐오하는 사람이 이름 공간을 차지하기 때문에 내 첨자를 쓸 수 없습니다.

그러나 우리는 이것을 할 수 있습니다 :

print("Hello"[range: 1...3])
// out: ell 

프로젝트에 다음을 추가하십시오.

extension String {
    subscript(range: ClosedRange<Int>) -> String {
        get {
            let start = String.Index(utf16Offset: range.lowerBound, in: self)
            let end = String.Index(utf16Offset: range.upperBound, in: self)
            return String(self[start...end])
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.