Sponsor Link
環境&対象
- macOS Monterey 12.1 Beta
- Xcode 13.1
- iOS 15
String/NSString との関係も含めまとめなおした記事を書きました。読みやすい方をどうぞ。
[Swift] String の位置指定を 理解する(String/String.Index と NSString/Int)
[Swift] String の範囲指定を理解する (String/Range, NSString/NSRange)
Range と NSRange
Range
String や AttributedString 中の部分文字列を使用したり、確認したりする時に使うのが Range です。
String に特定文字列が含まれているか確認する例
let str = "Hello, world!"
if let range = str.range(of: "llo") {
print("\(str) has \(str[range])")
}
// output
Hello world has llo
ちなみに、どんなデータを持っているかと lowerBound, upperBound を確認してみると以下のようになっています。
let str = "Hello world"
let range = str.range(of: "llo")!
// (1)
print(str[range])
// (2)
print(range.lowerBound)
print(range.upperBound)
// print-out
llo // str[range]
Index(_rawBits: 131072) // range.lowerBound
Index(_rawBits: 327680) // range.upperBound
- Range を使って、subscriptアクセス([ ]のこと)すると、該当部分の文字列を取得することができます。
- Range のプロパティ lowerBound, upperBound をみても直感的にはわかりませんでした・・・
NSRange
NSRange も Range と同様に、NSString 中の部分文字列を利用したりする時に使われるものです。
NSString, NSRange の方が、String, Range よりも古くからあります。(String は、Swift にしか存在しないです)
let nsStr = NSString("Hello world")
let nsRange = nsStr.range(of: "llo")
// (1)
print(nsStr.substring(with: nsRange))
// (2)
print(nsRange)
// print-out
llo // nsStr.substring
{2, 3} // nsRange
- String と違い、subscript で部分文字列を取得することはできませんが、substring というメソッドが用意されています
- NSRange は 開始位置と文字列長という情報を持っているようです。文字列の2番目の文字(0スタートです)から3文字分ということを表しています
経緯
なんで 同じような型があるの? というのが最初の疑問点かと思います。
ソフトウェアでの文字列処理には長い歴史がありますので、詳細は別途調べてもらうとして、簡単にいうと いろいろな文字を処理できるようにしたところ、1文字 = 可変長 になってしまった。
そのため、当初 NSRange で扱っていたような Int を使った “X文字目” から “Y文字分” のような扱いができなくなってしまったということ(のよう)です。
1文字 = 1 char であれば、String 中の i 番目の文字の次の文字は、(i+1) 番目になりますが、可変長になってしまうと、単純に +1 しても NG です。
そこで Swift での String には、Range という新しい String 中の部分文字列を指定する型が作られました。
例えば、Swift で String 中の2文字目から3文字分を取得するためは以下のようになり、単純に +3 して・・・ という計算ではなくなっています。
let str = "Hello world"
let secondCharIndex = str.index(str.startIndex, offsetBy: 1)
let afterThreeCharIndex = str.index(secondCharIndex, offsetBy: 3)
print(str[secondCharIndex..<afterThreeCharIndex])
// print-out
ell
Range と NSRange の相互変換
Swift で使っている時に String と Range だけで済むと平和です。
ですが、NSString を使う必要が出てくるケースで、NSRange が必要となることがあります。例えば、NSTextView を NSAttributedString を使って、装飾表示するケースです。
そんな時に、Range を NSRange に変換したり、その逆の NSRange を Range に変換したりすることが必要になります。
それぞれに、initializer が用意されています。どちら方向の変換でも 対象としている String も渡す必要があります。
Range を NSRange に変換する
let str = "Hello world"
let range = str.range(of: "llo")!
let nsRange = NSRange(range, in: str) // Range -> NSRange
print(nsRange)
// print-out
{2, 3}
NSRange を Range に変換する
let nsStr = NSString("Hello world")
let nsRange = nsStr.range(of: "llo")
let range = Range(nsRange, in: nsStr as String)
print(range)
// print-out
Index(_rawBits: 131072)..<Index(_rawBits: 327680)
まとめ:Range と NSRange の相互変換
- Range と NSRange は、initializer で相互変換できる
- 相互変換時には、対象とした String/NSString が必要
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
Sponsor Link