1. ホーム
  2. swift

[解決済み] SwiftでString.Indexはどのように動作するのか

2022-03-15 06:55:35

質問

私はSwift 3で古いコードと答えを更新してきましたが、Swiftの文字列とインデックスに到達すると、物事を理解するのに苦労しています。

具体的には以下のようなことを試していました。

let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5) // error

ここで、2行目が次のようなエラーになっていました。

'advancedBy' は使用できません。インデックスを n ステップ進めるには、インデックスを生成した CharacterView インスタンスに対して 'index(_:offsetBy:)' を呼び出してください。

なるほど String には、以下のメソッドがあります。

str.index(after: String.Index)
str.index(before: String.Index)
str.index(String.Index, offsetBy: String.IndexDistance)
str.index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)

最初は本当に混乱したので、理解できるまで遊びました。これらがどのように使用されるかを示すために、以下に答えを追加しています。

解決方法は?

以下の例では、すべて

var str = "Hello, playground"

startIndexendIndex

  • startIndex は最初の文字のインデックス
  • endIndex はインデックス 最後の文字

// character
str[str.startIndex] // H
str[str.endIndex]   // error: after last character

// range
let range = str.startIndex..<str.endIndex
str[range]  // "Hello, playground"

Swift 4の 片側範囲 であれば,以下のいずれかの形式に簡略化することができる.

let range = str.startIndex...
let range = ..<str.endIndex

以下の例では、わかりやすくするために完全な形を使いますが、読みやすくするために、おそらくあなたのコードでは片側だけの範囲を使いたいと思うことでしょう。

after

のように。 index(after: String.Index)

  • after は、指定されたインデックスの直後の文字のインデックスを参照します。

// character
let index = str.index(after: str.startIndex)
str[index]  // "e"

// range
let range = str.index(after: str.startIndex)..<str.endIndex
str[range]  // "ello, playground"

before

のように。 index(before: String.Index)

  • before は、指定されたインデックスの直前の文字のインデックスを参照します。

// character
let index = str.index(before: str.endIndex)
str[index]  // d

// range
let range = str.startIndex..<str.index(before: str.endIndex)
str[range]  // Hello, playgroun

offsetBy

のように。 index(String.Index, offsetBy: String.IndexDistance)

  • offsetBy の値は正でも負でもよく、与えられたインデックスから始まる。この型は String.IndexDistance を与えることができます。 Int .

// character
let index = str.index(str.startIndex, offsetBy: 7)
str[index]  // p

// range
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..<end
str[range]  // play

limitedBy

のように。 index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)

  • limitedBy は、オフセットによってインデックスが境界外に出ないことを確認するのに便利です。これは、バウンディングインデックスです。オフセットが制限を超える可能性があるため、このメソッドは Optional を返します。を返します。 nil は、インデックスが境界外である場合

// character
if let index = str.index(str.startIndex, offsetBy: 7, limitedBy: str.endIndex) {
    str[index]  // p
}

もしオフセットが 77 ではなく 7 の場合、その後に if 文はスキップされたはずです。

なぜString.Indexが必要なのですか?

それは 大いに を使用する方が簡単です。 Int を文字列のインデックスとして使用します。を新たに作成しなければならないのは String.Index は、Swift の文字がすべて同じ長さではないことです。Swift の単一の文字は、1つ、2つ、あるいはそれ以上の Unicode コードポイントから構成されるかもしれません。したがって、それぞれのユニークな文字列は、その文字のインデックスを計算する必要があります。

この複雑さをIntインデックスの拡張で隠すことは可能ですが、私はそれをしたくありません。実際に何が起こっているのかを思い知らされるのは良いことです。