例えば"hogefugapiyo"という文字列があるとして、これを4文字ずつに分割するなら["hoge", "fuga", "piyo"]、5文字ずつに分割するなら["hogef", "ugapi", "yo"]を得たい。
Rubyであればscanメソッドを使って次のように実行できるということがわかった。
1str = "hogefugapiyo"
2# => "hogefugapiyo"
3
4str.scan(/.{1,5}/)
5# => ["hogef", "ugapi", "yo"]
参考
これと同じことをSwiftでやりたくなったが、どうやら全く同じようなことをする関数はなさそうに見えた。なのでSwift力が足りないながらも自前で書いてみたのでそれを晒してみる。
Rubyの用にArrayを返す関数を追加するのでもよかったが、SequenceTypeを使うほうがよさそうな気がしたので↓のように実装した。Stringにsubstr(Int)という関数を生やして、それを呼ぶとforループやmapとかが使えるという感じになった。
1extension String {
2 var length: Int { return count(self) }
3
4 internal class SubstringGenerator: GeneratorType {
5 typealias Element = String
6 let count: Int
7 let string: String
8 var i = 0
9
10 init(count: Int, string: String) {
11 if count <= 0 {
12 fatalError("'count' must be bigger than 0.")
13 }
14 self.count = count
15 self.string = string
16 }
17
18 func next() -> Element? {
19 if i < string.length {
20 var endIndex : String.Index
21 if i + count > string.length {
22 endIndex = string.endIndex
23 } else {
24 endIndex = advance(string.startIndex, i + count)
25 }
26
27 var range = advance(string.startIndex, i)..<endIndex
28 i += count
29 return string.substringWithRange(range)
30 } else {
31 return nil
32 }
33 }
34 }
35
36 internal class SubstringSequence : SequenceType {
37 let count: Int
38 let string : String
39 typealias Generator = SubstringGenerator
40
41 init(count: Int, string: String) {
42 self.count = count
43 self.string = string
44 }
45
46 func generate() -> Generator {
47 return Generator(count: count, string: string)
48 }
49 }
50
51 func substr(count: Int) -> SubstringSequence {
52 return SubstringSequence(count: count, string: self)
53 }
54}
このsubstrを実際に使ってみるとこうなる。
1for substr in "hogefugapiyo".substr(5) {
2 println(substr)
3}
4// hogef
5// ugapi
6// yo
7
8var array = map("hogefugapiyo".substr(5)) {$0}
9println(array)
10// [hogef, ugapi, yo]
どうですかね、これ。
