Swift 4 になって、またまた文字列の扱いが変わりました。
その中でも、今回は表題の通り、Swift 4 の String から substring 的にインデックスを指定して部分文字列を取り出す方法を見ていきましょう。
ちなみに、Swift 2 から 3 に変わった時の substring については、以下の記事をご覧ください。
読み込み中です。少々お待ち下さい
Swift 4 の Substring
Swift 4 の String では substring は非推奨になっています(後述しますが、部分文字列という意味での Substring は存在します)。
Swift 3 時代の substring が残っていると、例えば以下のように怒られてしまうでしょう。
- 'substring(to:)' is deprecated: Please use String slicing subscript with a 'partial range upto' operator.
- 'substring(from:)' is deprecated: Please use String slicing subscript with a 'partial range from' operator.
- 'substring(with:)' is deprecated: Please use String slicing subscript.
deprecated なので、いちおうそのままでも使えるのですが、Swift 4 で文字列を部分的に取り出したい場合は、とりあえず以下のように書けば警告されないようになる筈。
let text = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
print(text[..<text.index(text.startIndex, offsetBy: 6)])
// -> "ABCDEF" 先頭から指定インデックスまで
print(text[text.index(text.startIndex, offsetBy: 20)...])
// -> "UVWXYZ" 指定インデックスから終端まで
print(text[text.index(text.endIndex, offsetBy: -6)...])
// -> "UVWXYZ" 指定インデックスから終端まで、その2
print(text[text.index(text.startIndex, offsetBy: 10)..<text.index(text.endIndex, offsetBy: -10)])
// -> "KLMNOP" 開始~終了指定
// Swift 3 と異なり「...」も使えるようになりました
print(text[text.index(text.startIndex, offsetBy: 10)...text.index(text.endIndex, offsetBy: -10)])
// -> "KLMNOPQ"
// 以下のように切り出すことも可能です
if let startIndex = text.index(of: "K"), let endIndex = text.index(of: "P") {
print(text[startIndex...endIndex])
// -> "KLMNOP"
}
// start - end ではなく長さで指定したい場合のサンプル
let startIndex = text.index(text.startIndex, offsetBy: 2) // 開始位置 2
let endIndex = text.index(startIndex, offsetBy: 6) // 長さ 6
print(text[startIndex..<endIndex])
// -> "CDEFGH"
// 以下のように不正なインデックスを指定すると、アプリが落ちてしまうので気をつけましょう
//print(text[text.index(text.startIndex, offsetBy: 32)...])
// 必要に応じて limitedBy を指定すれば、index が不正な場合は nil が返されます
if let index = text.index(text.startIndex, offsetBy: 32, limitedBy: text.endIndex) {
print(text[index...])
}
// 部分的に取り出した結果は文字列(String)ではなく部分文字列(Substring)です
// 文字列とほぼ同様に扱えますが、必要に応じて String に変換しましょう
let substr = text[..<text.index(text.startIndex, offsetBy: 6)]
print("substr = \(substr) / \(type(of:substr))")
// -> "substr = ABCDEF / Substring"
let str = String(substr)
print("str = \(str) / \(type(of:str))")
// -> "str = ABCDEF / String"
他にもっと良い書き方があったら、すみません。
Swift 3 時代から substring を使わずに range で指定しておくべきだったと、いまさらのように思ったり思わなかったり。
補足:
コード部分の一番最後に書いたように、部分的に取り出した結果は文字列(String)ではなく、実際は部分文字列(Substring)です。
文字列とほぼ同様に扱えますが、元の文字列のメモリを再利用する為、部分文字列が使用されている限り元の文字列全体がメモリに保持されている必要があります(long-term storage には向きません)。
取り出した部分だけを長期間保持する場合など、必要に応じて Substring として持たずに String に変換しましょう。
詳しくは、本家のドキュメントをどうぞ。
おわりに
ここが変わるのは想定の範囲内だった訳でして、思ったよりはマシな感じ。
まぁ、今回は Swift 3 → 4 への変換を強制される訳ではなく、Build Settings の Swift Language Version で 3.2 を指定できるので、既存のアプリは当面 Swift 3 で行きますけどね。
もちろん新しいアプリでは Swift 4 を使う予定ですが、ちょっともう付き合いきれなくなってきたので、今後は Visual Studio で C# を使えばいいんじゃないかなーとも思ってます(笑)