(Python)標準出力に CSV を書きだす
標準の csv モジュールは reader や writer を「ファイル様」オブジェクトにかぶせるようになっている。 UNIX 的な CLI のように標準出力に CSV を書きだしたいときはこうするとよさそう。
config = {'encoding': 'utf-8', 'newline': '', 'write_through': True}
writer = csv.writer(io.TextIOWrapper(sys.stdout.buffer, **config))
writer.writerow(['your', 'comma', 'separated', 'values'])
* * *
csv.writer に直接 sys.stdout を渡してもよさそうだけれど、これは csv モジュールが内部でかかえる universal newline handling とうまく噛みあわない。 Windows での話だけれど、出力をファイルにリダイレクトしたところ行ごとに余計に謎の改行が出力される。
リダイレクトしたファイルをバイナリーで見ると行末が \r\r\n (CR CR LF)になっている。 Windows では csv.writer.writerow は行末に \r\n のシーケンスを出力するようで、この \n が \r\n と再解釈されるものだから \r がひとつ余分に入ってしまうのだろう。
先に示したコードでは sys.stdout の無加工 bytes 出力用バッファーに直接 csv モジュールの出力を流しこむための小細工。「csv.writer の第一引数がファイルオブジェクトの場合は newline='' で開いておくこと」というドキュメントの要請にしたがうために、これを指定した TextIOWrapper を噛ませている。
* * *
io.StringIO(newline='') で生成したオブジェクトを csv.writer に渡してもよい。 CSV 加工、つまり必要なだけの csv.writer.writerow 呼びだしを終えたところで StringIO オブジェクトから getvalue() して結果を取りだし、これを 'utf-8' に encode した上で sys.stdout.buffer オブジェクトの write に回す。
ここでも write したデータを加工しない buffer オブジェクトを経由する必要がある。 sys.stdout に write したり、あるいは print すると \r\r\n の CR 増殖問題が起きてしまう。