2021/02/02 22:00追記
pythonのファイル入出力について勘違いがあった点を修正。
ついでにその勘違いを含んでいたタイトルも変更。
要点
Python3のcsvモジュールは便利ではあるが、エラーチェックが不要なら自分で実装するほうが早い。
調べたきっかけ
以前csvを変換する速度を調べていたが、最近セルの中にカンマが含まれているようなcsvに出会ってしまった。
実装を考えるのが面倒だったため、csvモジュールを調べながら実装した。
使い方
あれこれ書くより、次のコードを見るほうが早そうだ。
下記はpythonのコンソールに書いたときの使用例。
>>> import csv
>>> l = '"abc",1,4.5,"d,ef"'
>>> for row in csv.reader([l]):
... print(row)
...
['abc', '1', '4.5', 'd,ef']
>>> for row in csv.DictReader(["a,b,c,d",l]):
... print(row)
...
{'a': 'abc', 'b': '1', 'c': '4.5', 'd': 'd,ef'}
csv.reader()関数の中に、文字列を返すイテレータブルな変数を入れれば、各行を解釈したものを、リスト形式で返してくれる。
文字列のリストでなくても、例えばテキストファイルのファイルオブジェクトでも成立する。
速度チェック
以前の速度チェックに使ったソフトを流用して速度を確認した。
下記はテストに使ったコードで、split周りがなくなってスッキリしている、のだが……?
import datetime
import numpy as np
import csv
ROW = 10 ** 5
COL = 1000
def read_test_0():
dest = np.zeros((ROW, COL), dtype=bool)
with open("TestFiles\\case00.csv", "r") as f:
for row_index, l in enumerate(f):
dest[row_index, row_index % COL] = True
print(dest.sum())
def read_test_6():
dest = np.zeros((ROW, COL), dtype=bool)
with open("TestFiles\\case00.csv", "r") as f:
for row_index, l in enumerate(f):
row = l.split(",")
for col_index, val in enumerate(row):
dest[row_index, col_index] = (val == "1")
print(dest.sum())
def read_test_7():
dest = np.zeros((ROW, COL), dtype=bool)
with open("TestFiles\\case00.csv", "r") as f:
csv_data = csv.reader(f)
for row_index, row in enumerate(csv_data):
for col_index, val in enumerate(row):
dest[row_index, col_index] = (val == "1")
print(dest.sum())
def test_str_benchmark_2():
start = datetime.datetime.now()
read_test_0()
end0 = datetime.datetime.now()
read_test_6()
end1 = datetime.datetime.now()
read_test_7()
end2 = datetime.datetime.now()
print(f"読み込み時間\n{(end0-start).total_seconds():.3}秒")
print(f"最初の文字だけの読み取り1\n{(end1-end0).total_seconds():.4}秒")
print(f"最初の文字だけの読み取り2\n{(end2-end1).total_seconds():.4}秒")
if __name__ == "__main__":
test_str_benchmark_2()
実行結果は何故かこうなった。
100000
24944449
24994288
読み込み時間
1.57秒
最初の文字だけの読み取り1
15.61秒
最初の文字だけの読み取り2
18.39秒
読み込み速度はおそらく、冒頭で述べた通りダブルクオーテーションマークで囲んだときなどの処理が追加されたからだろう。
だが同じファイルを読み込んだはずなのに出力が異なるのはおかしい。追加で調査をすることにした。
追加の調査結果
なんとなく心当たりがあったので、read_test_6()関数の適当なところでブレークポイントを作り、row[-1]を確認した。
結果、'1\n'が出力された。
自分の環境ではopen("~~", "w")でファイルを書き込むと、CRLFの改行コードを持ったテキストファイルが出力され、読み込むときはなぜかLFのみの改行コードとして読み込まれるようだ。
ファイルの読み込みと書き込みで形式が違うのはバグっぽい挙動だが、とりあえずrow = l.split(",")
を、row = l.strip().split(",")
に変更することでcsvモジュールと同様の出力を確認できた。
2021/02/02 22:00追記
Pythonのreadline()はC#のReadLine()と違って、戻り値の文字列には改行を含むらしい。
rstrip関数を使って、文末の"\n"を取り除く必要があるようだ。