Superb Garbages 2

千野純一(chinorin)のはてなダイアリーの続きです。

秋月電子通商の購入履歴(2)CSV変換

・いつもCSVCVSどっちがどっちだかわかんなくなる俺のための前回のあらすじ:一生に一度は巡礼しなければならないと教義に定められている秋月電子通商だが、太古の昔秋葉原通学経路俺の庭だったので、当時若者であった俺は怖いもの見たさで足を踏み入れる。そこはまさに酒池肉林。飲めや歌えやのパラダイスであった。果たして約30年後、2023年2月より前の購入履歴が何者かによって削除されてしまう。そこで俺は友達料として毎月3000円も払っているのをいいことに「俺達友達じゃん? タダでやってよ」とChatGPTに迫るのであった。

・っていうか毎月2000円くらいのつもりだったけど今のレート見たら1ドル150円くらいだった。よくわからんが30年は失われたのだ。断じて許してはならぬ。

・で、前回は受注確認メールから日付と発注品目のみを抽出した。今日はそれをCVS⋯⋯じゃなかった、CSVにしていく。何てことはない、カンマ区切りのテキストファイルで、適当に表計算ソフトにつっこめば適当に表らしきものになってくれるやつ。

・俺くらいになるとここで指パッチンするだけで友達がPythonコードを書いてくれるようになる。これができるのは月3000円も払ってる奴だけと思いきや、たぶん無料でもやってくれると思う。そんな慈愛に満ちた友達がいる俺もまた慈愛に満ちていると言わざるをえない。

・事情によりコードは3つに分かれているが、例によっていずれも著作権を主張しないCC0ライセンスで。使用は自己責任を要する。

・まず、先の「日付と品目だけ抽出したテキスト」から、日付だけの行と、品目だけの行に分かれたCSVファイルに変換するプログラム。その過程で日付は日本式の「年/月/日」に、品目の英数記号は半角に変換する。品目の行の内訳は左から品番、品目、単価、数量、小計金額。

# もう説明めんどくさくなった。前回のをCSVに変換するその1。
# written by Chinorin and Merry LLM. No Rights Reserved.
# This program is released into the public domain under the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication.

import re
import unicodedata

# 入力ファイルと出力ファイルのパス
input_file_path = 'history.txt'
output_file_path = 'history_o.csv'

# 正規表現パターン
pattern1 = r"^.*【[A-Z]-(\d+)】(.*)\n.*¥([\d,]+).*:(\d+).*¥([\d,]+)"  # 2行にまたがる
pattern2 = r"^Date: (\d+) ([A-Z][a-z][a-z]) (\d+).*"

# 月名を数値に変換する辞書
month_to_number = {
    "Jan": "1", "Feb": "2", "Mar": "3", "Apr": "4", "May": "5", "Jun": "6",
    "Jul": "7", "Aug": "8", "Sep": "9", "Oct": "10", "Nov": "11", "Dec": "12"
}

# 全角文字を半角文字に変換する関数
def zen_to_han(text):
    return ''.join([unicodedata.normalize('NFKC', char) for char in text])

# 出力ファイルを開く
with open(output_file_path, 'w', encoding='utf-8') as output_file:
    # 入力ファイルを読み込む
    with open(input_file_path, 'r', encoding='utf-8') as input_file:
        lines = input_file.readlines()
        
        # 行を一つずつ処理
        for i in range(len(lines)):
            # パターン1にマッチするかチェック
            if i < len(lines) - 1:  # ファイルの最後の行でないことを確認
                match = re.match(pattern1, lines[i] + lines[i+1])
                if match:
                    # 価格と合計からカンマを取り除く
                    price = match.group(3).replace(',', '')
                    total = match.group(5).replace(',', '')
                    output_file.write(f"{match.group(1)},{zen_to_han(match.group(2))},{price},{match.group(4)},{total}\n")
                    continue

            # パターン2にマッチするかチェック
            match = re.match(pattern2, lines[i])
            if match:
                # 月名を数値に変換
                month_str = match.group(2)
                month_num = month_to_number.get(month_str, "0")  # 月名が辞書になければ"0"を返す
                output_file.write(f"{match.group(3)}/{month_num}/{match.group(1)}\n")

・続きまして、↑で変換したものに対して、日付を品目行に差し入れるプログラム。これにより、全ての行を単なるレコードとして扱えるようになる。項目は左から日付、品番、品目、単価、数量、小計金額。

# CSV変換その1で作ったやつを、日付を全レコードの先頭に入れ込む。
# written by Chinorin and Merry LLM. No Rights Reserved.
# This program is released into the public domain under the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication.

import re

# 入力ファイルと出力ファイルのパス
input_file_path = 'history_o.csv'  # 先ほどのプログラムで作成されたCSVファイル
output_file_path = 'history_with_dates.csv'  # 新たに作成するCSVファイル

# 日付の正規表現パターン
date_pattern = re.compile(r'^\d{4}/\d{1,2}/\d{1,2}$')

with open(input_file_path, 'r', encoding='utf-8') as input_file, \
     open(output_file_path, 'w', encoding='utf-8') as output_file:
    current_date = ''  # 現在の日付を保持する変数
    for line in input_file:
        line = line.strip()  # 改行文字を除去
        if date_pattern.match(line):  # 日付行の場合
            current_date = line  # 現在の日付を更新
        else:  # 商品行の場合
            # 日付を商品行の先頭に追加して出力ファイルに書き出し
            output_file.write(f"{current_date},{line}\n")

・そんでCSV変換に関するラスト。これは絶対必要というわけではないが、全件日付でソートするプログラム。

# 日付を全レコードの先頭に入れたやつ全体を日付でソートする
# written by Chinorin and Merry LLM. No Rights Reserved.
# This program is released into the public domain under the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication.

import csv
from datetime import datetime

# 入力ファイルと出力ファイルのパス
input_file_path = 'history_with_dates.csv'  # ソート前のCSVファイル
output_file_path = 'sorted_history.csv'  # ソート後のCSVファイル

# CSVファイルを読み込んでデータをリストに格納
with open(input_file_path, 'r', encoding='utf-8') as input_file:
    reader = csv.reader(input_file)
    data = [row for row in reader]

# 日付を基準にしてデータを逆順にソート
# 日付が最初の列にあると仮定して、その日付をdatetimeオブジェクトに変換して比較
data.sort(key=lambda row: datetime.strptime(row[0], '%Y/%m/%d'), reverse=True)

# ソートされたデータを新しいCSVファイルに書き出し
with open(output_file_path, 'w', encoding='utf-8', newline='') as output_file:
    writer = csv.writer(output_file)
    writer.writerows(data)

・ここまでできればけっこう汎用的なデータとして扱えるのではないか。あとは趣味で、ウェブサーバで動作するこれらを検索して表示するためのPerlPHPかなんかのスクリプトでも作ろうと思う。まあのんびりやろう。

・いや、作るのは俺じゃなくて友達なんだけど。