Swiftで範囲を作成するにはどうすればよいですか?


103

Objective-cでは、NSRangeを使用して範囲を作成します

NSRange range;

それでは、Swiftで範囲を作成する方法は?


3
範囲は何のために必要ですか?Swiftの文字列はを使用しますがRange<String.Index>NSStringand を使用する必要がある場合があるNSRangeため、さらにコンテキストが役立つ場合があります。–しかし、stackoverflow.com / questions / 24092884 /…をご覧ください
マーティンR

回答:


256

Swift 4用に更新

Swiftの範囲はより複雑でありNSRange、Swift 3では簡単にはなりませんでした。この複雑さの一部の背後にある理由を理解したい場合は、こちらこちらをお読みください。それらを作成する方法と、いつ使用するかを説明します。

閉じた範囲: a...b

この範囲演算子は、がタイプなどの可能な最大値であっても、element a elementの両方を含むSwift範囲を作成します。閉じた範囲には、との2種類があります。bbInt.maxClosedRangeCountableClosedRange

1。 ClosedRange

Swiftのすべての範囲の要素は比較可能です(つまり、それらは比較可能なプロトコルに準拠しています)。これにより、コレクションから範囲内の要素にアクセスできます。次に例を示します。

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。上記と同様に、ハーフオープンレンジには2つの異なるタイプがRangeありCountableRangeます。

1。 Range

と同様にClosedRange、を使用してコレクションの要素にアクセスできますRange。例:

let myRange: Range = 1..<3

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

ただし、繰り返しますが、 Range可能ではありません。

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

NSRangeSwiftでは(たとえば、属性付き文字列を作成するときなど)時々(必須)使用できるため、作成方法を知っておくと役立ちます。

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"!?

スマイリーフェイスの保存には2つのUTF-16コードユニットが必要なので、「d」が含まれていないという予期しない結果が生じます。

Swiftソリューション

このため、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の苦痛ですが、絵文字やその他のUnicodeスカラーをより適切に処理することができます。

さらなる研究


私のコメントは、「NSRangeの問題」サブセクションに関するものです。NSString文字を内部的にUTF-16エンコーディングで格納します。完全なUnicodeスカラーは21ビットです。ニヤリ顔の文字(U+1F600)は単一の16ビットコード単位に格納できないため、16ビットコード単位にNSRange基づいて2 カウントに広がります。この例では、たまたま2つの文字のみを表す3つのコード単位
コードが異なる

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)

出力:こんにちは


「範囲」データ型があるのがわかります。それでは、どうすれば使用できますか?
vichhai

@vichhai使用したい場所について詳しく説明できますか?
Dharmbir Singh、2015

目標cのように:NSRange範囲。
ヴィチャイ

1

Swift 4でも、Intを使用して文字列の範囲を表現する簡単なネイティブな方法がまだないのは驚くべきことです。範囲でサブストリングを取得する方法としてIntを提供できる唯一のStringメソッドはprefix、およびsuffixです。

いくつかの変換ユーティリティを手元に用意しておくと、文字列と話すときに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)"Range<String.Index>の最後の文字を採用します"hello"

Range<String.Index>Cocoaと通信する必要があるときに(たとえば、NSAttributedString属性の範囲を処理する場合)、をNSRange に変換することも役立ちます。Swift 4はそれを行うネイティブな方法を提供します:

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

したがって、別のユーティリティを作成して、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の範囲が作成されます


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

0
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)
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.