ExcelにやられたCSVを手当てする

今日、Excelで編集してしまったために、おかしくなってしまったCSVが持ち込まれたのでした。ご存じの方もいらっしゃいましょうが、ExcelCSVを読み込む際に、0埋めして桁をそろえた数字列を見事にただの数値にしてくれる。日付らしい文字列があったら見事に日付として処理して、もとの体裁を崩して保存してくれる。実におそろしいアプリケーションなのであります。各フィールドの頭にシングルクオートをひとつ打っておくと、たとえそれが数値であっても文字列として解釈してはくれるのだけれども、それはあくまでExcelにおける作法であって、それがそのまま他の処理系に通用するなんて話はない。

まあ、そうしたCSVデータをなんとかもとにもどしておくれといわれたんですね。で、私はCSVを処理する際にはPythonを便利に使っていて、なにしろPythonにはCSVを扱うためのモジュール、その名もCSVなんてものが用意されています。そいつを使って、なんとかシステムが要求する体裁を復旧させようというのです。

今回の対処は三種類。ひとつ目は、0埋めされた1桁の数値が1桁にされてしまっているので、それを戻す。ただし、同じフィールドにはアルファベット1文字の値が存在することがわかっているから、単純に0をつければいいってわけじゃない。これは、正規表現で対処することにしました。

ふたつ目は、日付の形式。月と日が一桁であっても0埋め2桁で表現していた。とこれが、それを1桁の数字にされてしまった。それを戻します。

みっつ目は、0埋め6桁の数値が……、まあ、もう説明はいらんですね。

以上のみっつを関数にして、手当てが必要なフィールドにあててやることにしました。それが以下のスクリプト。どういう手当てが必要か、正しいCSVと誤ったCSV(ただし内容は編集済)を比較して把握するのに時間を食ったものの、スクリプトを書くこと自体はさほどの時間もとらず、こうしたちょこっとした作業にこうした言語はとてつもなく便利であると思います。

Python 3.0で実行することを前提に書かれたものなので、2.6ではうまくいかないかも知れません。

# -*- coding: utf-8 -*-
import codecs
import csv
import re

sd = re.compile('^\d$')
sla = re.compile('/')

def touch_d(s):
    if sd.match(s):
        s =  '0' + s
    return s

def touch_date(s):
    dl = sla.split(s)
    while len(dl[0]) < 4:
        dl[0] = '0' + dl[0]
    while len(dl[1]) < 2:
        dl[1] = '0' + dl[1]
    while len(dl[2]) < 2:
        dl[2] = '0' + dl[2]
    return '/'.join(dl)

def touch_len(s, digit=6):
    if not isinstance(digit, int):
        raise TypeError('need int as digit, got %r' % \
                        type(digit))
    while len(s) < digit:
        s = '0' + s
    return s

result = list()
with codecs.open('s.csv', 'r', 'shift_jis') as f:
    reader = csv.reader(f)
    for row in reader:
        row[0] = touch_d(row[0])
        row[2] = touch_date(row[2])
        row[3] = touch_date(row[3])
        row[4] = touch_d(row[4])
        row[20] = touch_len(row[20], 6)
        result.append(row)

csv.register_dialect('simple', delimiter=',', quoting=csv.QUOTE_NONE)
with codecs.open('r.csv', 'w', 'shift_jis') as f:
    writer = csv.writer(f, 'simple')
    writer.writerows(result)

print('done')