Python で文字列をN文字ずつに分割する
Pythonの文字列に対する split は、指定したセパレータで分割することはできますが、指定文字数で分割することはできません(と思います)。そこでPythonで(できればPython的に)どう書いたらよいか考えてみました。
1文字ずつたどりながら処理する
ぱっと思いつくのはこの方法です。
指定文字数に達したら結果用のリストに追加し、そのリストを返します。
def splitStr(str, num): s = '' j = 0 l = [] for i in str: s += i j += 1 if j == num: l.append(s) j = 0 s = '' if s: l.append(s) return l
zip で Python 的に処理する
上の方法はあまり Python的ではありませんでした。
zipを使う方がPython的かつ高速らしいので、zipを使う方法を考えてみました。
def splitStr2(str, num): l = [] for i in range(num): l.append(str[i::num]) l = ["".join(i) for i in zip(*l)] rem = len(str) % num # zip で捨てられた余り if rem: l.append(str[-rem:]) return l
もっとうまく書けそうですが、なんとなくPython的な気がします。
性能比較
せっかくなので性能を比較してみました。
timeit を利用して 10 万回呼び出した時間を計測しています。
$ python splitStr.py splitStr: 3.60764718056 # シンプルな方法 splitStr2: 2.22185182571 # zip を使った方法
結果は上の通り、zipを使った方法が 2.2 秒とシンプルな方法の 1.5 倍くらい高速でした。
zipはうまく使うと強力ですね。
以下は今回の計測に利用したスクリプト全文です。
#!/usr/bin/env python # -*- coding: utf-8 -*- import sys, timeit def splitStr(str, num): s = '' j = 0 l = [] for i in str: s += i j += 1 if j == num: l.append(s) j = 0 s = '' if s: l.append(s) return l def splitStr2(str, num): l = [] for i in range(num): l.append(str[i::num]) l = ["".join(i) for i in zip(*l)] rem = len(str) % num # zip で捨てられた余り if rem: l.append(str[-rem:]) return l if __name__ == '__main__': """ "a" 100 文字を 2 文字ずつのリストに分割 それを 10 万回繰り返して時間を計測""" t = timeit.Timer('splitStr("a" * 100, 2)', "from __main__ import splitStr") print "splitStr: " + str(t.timeit(number=100000)) t = timeit.Timer('splitStr2("a" * 100, 2)', "from __main__ import splitStr2") print "splitStr2: " + str(t.timeit(number=100000))