3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Swift5] 部分文字列を文字数から取得するときの注意点

Last updated at Posted at 2020-09-06

SwiftのStringは真剣にunicodeを扱っているらしく、大変難しいです。
参考:Swift の文字列の長さ - Qiita

部分文字列(ex:「アイウエオ」の「イウエ」)の取得も例外ではありません。Swift4までのやり方とは変わってしまい、調べてもなかなか出てこないのでメモしておきます。

追記

よく考えるとこれが一番スマートでした。返ってくるのはSubStringでパフォーマンス上のメリットもありそうです。

let string = "✨💪🥺✨"
let partialString = string[string.index(string.startIndex, offsetBy: 1)...string.index(string.startIndex, offsetBy: 2)]
print(partialString) //"💪🥺"

本文

まず、求めるのは「i番目の文字からj番目の文字まで」とします。例えばi=1, j=3string = "アイウエオ"ならばpartialString = イウエとなります。
以下は誤りです。

let partialString = [String.Index(utf16Offset: i, in: string)...String.Index(utf16Offset: j, in: string)]

私は最初うっかりしてこの方法を使っていたのですが、これだと次のような例でバグが起こります。

let string = "✨💪🥺✨"
let partialString = [String.Index(utf16Offset: 1, in: string)...String.Index(utf16Offset: 2, in: string)]
print(partialString) //"💪"

なぜこうなってしまうのかというと、utf16Offsetとしっかり書いてある通り、あくまでここで得ているIndexutf16でのものだからです。実際

print("✨💪🥺✨".utf16.count) //6

なので、納得のいく結果でした。
確実に文字数単位で取り出したいときは、一度文字ごとに分割してから改めて次のようにします。

let string = "✨💪🥺✨"
let partialString = String(string.map{$0}[1...2])
print(partialString) //"💪🥺"

なかなか危ないところなので気をつけましょう。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?