PIYO - Tech & Life -

SwiftでN文字ずつに分割した文字列を得る方法

Swift

例えば"hogefugapiyo"という文字列があるとして、これを4文字ずつに分割するなら["hoge", "fuga", "piyo"]、5文字ずつに分割するなら["hogef", "ugapi", "yo"]を得たい。

Rubyであればscanメソッドを使って次のように実行できるということがわかった。

str = "hogefugapiyo"
# => "hogefugapiyo"

str.scan(/.{1,5}/)
# => ["hogef", "ugapi", "yo"]

参考

Ruby 文字列を任意の文字数に分割する - Qiita

これと同じことをSwiftでやりたくなったが、どうやら全く同じようなことをする関数はなさそうに見えた。なのでSwift力が足りないながらも自前で書いてみたのでそれを晒してみる。

Rubyの用にArrayを返す関数を追加するのでもよかったが、SequenceTypeを使うほうがよさそうな気がしたので↓のように実装した。Stringsubstr(Int)という関数を生やして、それを呼ぶとforループやmapとかが使えるという感じになった。

extension String {
    var length: Int { return count(self) }
    
    internal class SubstringGenerator: GeneratorType {
        typealias Element = String
        let count: Int
        let string: String
        var i = 0

        init(count: Int, string: String) {
            if count <= 0 {
                fatalError("'count' must be bigger than 0.")
            }
            self.count = count
            self.string = string
        }
        
        func next() -> Element? {
            if i < string.length {
                var endIndex : String.Index
                if i + count > string.length {
                    endIndex = string.endIndex
                } else {
                    endIndex = advance(string.startIndex, i + count)
                }
                
                var range = advance(string.startIndex, i)..<endIndex
                i += count
                return string.substringWithRange(range)
            } else {
                return nil
            }
        }
    }
    
    internal class SubstringSequence : SequenceType {
        let count: Int
        let string : String
        typealias Generator = SubstringGenerator
        
        init(count: Int, string: String) {
            self.count = count
            self.string = string
        }
        
        func generate() -> Generator {
            return Generator(count: count, string: string)
        }
    }
    
    func substr(count: Int) -> SubstringSequence {
        return SubstringSequence(count: count, string: self)
    }
}

このsubstrを実際に使ってみるとこうなる。

for substr in "hogefugapiyo".substr(5) {
    println(substr)
}
// hogef
// ugapi
// yo

var array = map("hogefugapiyo".substr(5)) {$0}
println(array)
// [hogef, ugapi, yo]

どうですかね、これ。