Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
59
Help us understand the problem. What is going on with this article?
@risuke

Swift Stringの切り出し、文字列操作について(Swift4のsubstring周りの挙動変更対応, Swift5対応バージョン, Swift5.3対応バージョン)

2020-10-09 追記

自分でも書いてて忘れたのでメモします。

var text = "Hello, playground"

let from = text.index(text.startIndex, offsetBy:0)
let to = text.index(text.startIndex, offsetBy:5)
let newString = String(text[from..<to])
print(newString)

-> Hello

クロージャーで書くとこんな感じ

let substr : (String, Int, Int) -> String = { text, from, length in
    let to = text.index(text.startIndex, offsetBy:from + length)
    let from = text.index(text.startIndex, offsetBy:from)
    return String(text[from...to])
}

let text = "Hello, playground"
let newString = substr(text, 1, 2)
print(newString)

2020/03/17 追記

Swift5だとまた変わってます。何回いじるんやほんま…

Swift5からはString()で囲めばOKです。

let text = text[from...to]

let text = String(text[from...to])

2017/11/07 追記

XCode9.1になってさらっとSwiftが4.0.2にバージョンアップしました。

Stirng.charactersがdeperecatedになっていました。
記憶違いかもしれませんが、確か4.0の時はdeprecatedではなかったと思うのですが...

というわけで下記の修正を行っています。

var hoge : String = "aaaa"

hoge.characters.length

hoge.count

本文

Swift4でそこまで変更ないと思っていましたが、Stringが再度Collectionになった結果地味に書き換える箇所が発生していました。

warningだけでは判断出来なかったので、調べた内容をメモしました。

Swift3 < 4までの書き方

var text1 = text.substring(from: text.index(text.startIndex, offsetBy: 1))

この書き方で書いた場合

'substring(from:)' is deprecated: Please use String slicing subscript with a 'partial range from' operator.

というwarningが出ます。

swift4からrangeを使ってsubstringをすればよい、ということですね。

復習:Swiftでのrangeの書き方

Swiftのrangeの書き方に未だに慣れていないのでまとめます。

1. 1から5まで

let one2five = 1...5

2. 1から5未満

let one2lessthanfive = 1..<5

3. 1以上

let morethanOne = 1...

4. 5以下

let lessthanFive = ...5

5. 5未満

let lessthanFive = ..<5

参考:https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html

substringでの利用法

let text : String = "abcdefg"
let text2 = text[1...]

こんな書き方が出来るような気がしたのですが、実際にはこれはエラーになります。

Stringでやる場合はString.Indexを使ってrangeを記述する必要があります。

  1. 1〜5文字目まで
let text1 = text[text.startIndex...text.index(text.startIndex, offsetBy: 4)]
let text2 = text[text.startIndex..<text.index(text.startIndex, offsetBy: 5)]
let text2 = text[..<text.index(text.startIndex, offsetBy: 5)]

2.2文字目から末尾まで

let text1 = text[text.index(text.startIndex, offsetBy: 1)...]
let text2 = text[text.index(text.startIndex, offsetBy: 1)...text.index(before: text.endIndex)]
let text3 = text[text.index(text.startIndex, offsetBy: 1)..<text.endIndex]

3.2〜4文字目

let text1 = text[text.index(text.startIndex, offsetBy: 1)...text.index(text.startIndex, offsetBy: 3)]
let text2 = text[text.index(text.startIndex, offsetBy: 1)..<text.index(text.startIndex, offsetBy: 4)]

let from = text.index(text.startIndex, offsetBy: 1)
let to = text.index(from, offsetBy: 2)
let text3 = text[from...to]

別の書き方

先頭から、又は末尾から限定であればより短い記述で記述することが出来ます。

1.先頭からx文字

let text1 = text.prefix(x)

2.末尾からx文字

let text1 = text.suffix(x)

マイナス値を指定することが出来ないので、末尾を1文字削る場合などは以下のように書きます。

let text1 = text.prefix(text.characters.count-1)

Range表記とprefix/suffixの違い

Range表記の場合、文字列の範囲外を指定した場合にfatalエラーが出て止まります。
do〜catchでエラーをキャッチすることも出来ないのでコードでチェックする必要がありそうです。

prefix/suffixの場合は長すぎた場合でもエラーが発生しないので、利用箇所は制限されますが、極力こちらを使うのが正解と考えられます。

Extensionで解決

text.substring(1...5)
text.substring(1..<5)
text.substring(1...)
text.substring(...5)

直感的にRangeで書くためのExtensionを作ってみました。

extension String {

    func substring(_ r: CountableRange<Int>) -> String {

        let length = self.count
        let fromIndex = (r.startIndex > 0) ? self.index(self.startIndex, offsetBy: r.startIndex) : self.startIndex
        let toIndex = (length > r.endIndex) ? self.index(self.startIndex, offsetBy: r.endIndex) : self.endIndex

        if fromIndex >= self.startIndex && toIndex <= self.endIndex {
            return String(self[fromIndex..<toIndex])
        }

        return String(self)
    }

    func substring(_ r: CountableClosedRange<Int>) -> String {

        let from = r.lowerBound
        let to = r.upperBound

        return self.substring(from..<(to+1))
    }

    func substring(_ r: CountablePartialRangeFrom<Int>) -> String {

        let from = r.lowerBound
        let to = self.count

        return self.substring(from..<to)
    }

    func substring(_ r: PartialRangeThrough<Int>) -> String {

        let from = 0
        let to = r.upperBound

        return self.substring(from..<to)
    }
}
59
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
risuke

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
59
Help us understand the problem. What is going on with this article?