web-dev-qa-db-ja.com

Swiftで範囲を作成する方法

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

NSRange range;

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

93
vichhai

Swift 4用に更新された

Swiftの範囲はNSRangename__よりも複雑で、Swift 3ではそれほど簡単にはなりませんでした。この複雑さの背後にある理由を理解したい場合は、 this および this 。I ')を読んでください。それらを作成する方法とそれらを使用する可能性があるときにだけあなたに示すでしょう。

閉鎖範囲:a...b

この 範囲演算子 は、aname__が(Int.maxのような)型で可能な最大値であっても、要素bname__element bname__の両方を含むSwift範囲を作成します。閉じた範囲にはClosedRangename__とCountableClosedRangename__の2種類があります。

1. ClosedRangename__

Swiftのすべての範囲の要素は同等です(つまり、それらはComparableプロトコルに準拠しています)。それはあなたがコレクションから範囲内の要素にアクセスすることを可能にします。これが一例です。

let myRange: ClosedRange = 1...3

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

ただし、ClosedRangename__はカウントできません(つまり、Sequenceプロトコルに準拠していません)。つまり、forname__ループで要素を反復処理することはできません。そのためにはCountableClosedRangename__が必要です。

2. CountableClosedRangename__

これは最後のものと似ていますが、今度は範囲を繰り返すこともできます。

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

この範囲演算子には、要素aname__が含まれますが、notelement bname__が含まれます。上記のように、2つの異なるタイプの半開き範囲があります:Rangename__およびCountableRangename__。

1. Rangename__

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

let myRange: Range = 1..<3

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

ただし、ここでもRangename__を反復処理することはできません。これは比較可能なものであり、厳密ではないためです。

2. CountableRangename__

CountableRangename__は反復を許可します。

let myRange: CountableRange = 1..<3

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

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

NSRange

SwiftではNSRangename__を( 属性付き文字列 など)を作成するときに)使用することができる(しなければならない)ので、作成方法を知っておくと便利です。

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

これはlocationであり、lengthであり、開始インデックスと終了インデックスではないことに注意してください。この例はSwiftの範囲3..<5と意味が似ています。ただし、種類が異なるため、互換性はありません。

文字列付きの範囲

...および..<の範囲演算子は範囲を作成するための簡単な方法です。例えば:

let myRange = 1..<3

同じ範囲を作成するための手間のかかる方法は、

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

ここでのインデックスタイプはIntname__です。ただし、Stringname__では文字列が文字で構成され、すべての文字が同じサイズではないため、これは機能しません。 (詳細は this を読んでください。)例えば、????のような絵文字は、文字 "b"よりも多くのスペースを取ります。

NSRangeの問題

NSRangename__とNSStringname__を絵文字で試してみると、私の言っていることがわかります。頭痛。

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 Solution

このため、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

片側整数範囲を使用してコレクションを反復処理できます。これは documentation )の例です。

// 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スカラーの処理が向上する可能性があります。

さらなる研究

222
Suragch

Xcode 8 beta 2•Swift

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.

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
25
Leo Dabus

(1 .. <10)

返します...

範囲= 1 .. <10

3
Victor Lin

こんな感じ

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)

出力:こんにちは

2
Dharmbir Singh

誰でもNSRangeオブジェクトを作成したい場合は、次のように作成できます。

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

これにより、位置0、長さ5の範囲が作成されます。

1
Van

Swift 4でも、Intを使用してStringの範囲を表現するための単純なネイティブの方法がまだないことは驚くべきことです。範囲を指定して部分文字列を取得する方法としてIntを指定できるようにする唯一のStringメソッドは、prefixsuffixです。

Stringと話すときに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"です。

Cocoaと対話しなければならないとき(たとえば、NSAttributedString属性の範囲を扱う場合)には、Range<String.Index>を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
matt

私は以下の拡張子を作成しました:

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
dr OX

こんな感じで使えます

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
Mohammad Nurdin
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
Abo3atef