例えば"hogefugapiyo"
という文字列があるとして、これを4文字ずつに分割するなら["hoge", "fuga", "piyo"]
、5文字ずつに分割するなら["hogef", "ugapi", "yo"]
を得たい。
Rubyであればscanメソッドを使って次のように実行できるということがわかった。
str = "hogefugapiyo"
# => "hogefugapiyo"
str.scan(/.{1,5}/)
# => ["hogef", "ugapi", "yo"]
参考
これと同じことをSwiftでやりたくなったが、どうやら全く同じようなことをする関数はなさそうに見えた。なのでSwift力が足りないながらも自前で書いてみたのでそれを晒してみる。
Rubyの用にArrayを返す関数を追加するのでもよかったが、SequenceTypeを使うほうがよさそうな気がしたので↓のように実装した。String
にsubstr(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]
どうですかね、これ。