タイトルに通り、PythonでCSVから入力してjsonに出力してexeにする時の注意点を備忘録がてら記しておきます。実装例は完全に個人用のツールなので、概要だけ話します。重要なのはエラーメッセージと対処法。
デコードエラーを起こすCSV
まずcsvを読み込みます。input.csv
というcsvにデータを用意しておいて、それを読み取ります。
import csv
csv_file = open("./input.csv", "r")
c = csv.DictReader(csv_file)
いきなりですが、ここでcsvの中身を見ようとすると(たぶん)以下のようなエラーになります。
Exception has occurred: UnicodeDecodeError
'cp932' codec can't decode byte 0xef in position 0: illegal multibyte sequence
File "C:\Users\hoge\fuga.py", line 19, in <module>
for row in c:
デコードできないと叱られます。これはpythonをutf-8
のファイルをshift-jis
にしようとするからです。以下のように修正する必要があります。
csv_file = open("input.csv", "r",encoding="utf-8-sig")
全て同じデータになるjson
今回いちばん面食らった部分。
#csvからパラメータだけ抜粋
params = []
for row in c:
points.append(row)
n = len(params)
#jsonを読み込んでパラメータの枠を確保する
import json
json_open = open('sample.json', 'r')
j = json.load(json_open)
for i in range(n-1):
j["Movements"].append(j["Movements"][0])
下の段で少し不格好なことをしてまして、それがトラブルの元だったのですが、sample.json
にはデータの雛形がありまして、それをパラメータの行数n
まで拡張するために、雛形をn-1回コピーするという方法を取りました。この結果、無事にj
というjsonデータのMovements
というキーにn
個の枠ができたので、後はfor
文をひたすら回してデータを入れていきます。
……が、データを入れ終わった後、なぜかすべての行が同じデータになるという謎の結果に悩まされました。
少しデバッグした結果、あらゆる入力ですべての行が書き換えられてしまうということがわかり、これはすべての行が同じデータを参照しているということがわかりました。見かけのデータはn
行でも、すべて1つのデータを参照しているだけであり、どれか1つに改変を加えたらすべてのデータが影響を受けるのです。
なぜこんなことになってしまったのか。
int
やstr
のような型の場合、=
で変数をコピーしても、なにか変更を加えた時点で「別の変数」と見なされてIDが割り振られますが、list
は=
で変数をコピー(実はコピーできていない)した後に編集したらオリジナルデータが編集されるのです。……というのは知識としては知っていましたが、dict
もそうでしたね。解決策としては、ハードコピーという手法で変数をコピーするしかないです。
#csvからパラメータだけ抜粋
params = []
for row in c:
points.append(row)
n = len(params)
#jsonを読み込んでパラメータの枠を確保する
import json
import copy
json_open = open('sample.json', 'r')
j = json.load(json_open)
for i in range(n-1):
new_j = copy.deepcopy(j["Movements"][0])
j["Movements"].append(new_j)
今回の事例では、ただのcopy
(浅いコピー)ではなくdeepcopy
(深いコピー)でないとダメでした。
動かないexe
Pythonを持ってない人でも動かせるようにexe化してくれるモジュールで、pyinstaller
というものがあります。これをpip
しようと思ったらなぜかエラーでインストールできず、ぐぐったらpipのバージョンダウン(20
→18
)すれば良いということなのでそれで成功しました。今それを再現しようとしたらなぜか最新のpipでも成功してしまったのでエラー再現不可能です。申し訳ない。
それで無事pyinstaller
を使ってexe化したのですが、実行してもすぐに終了してしまう。デバッグ用に入れた先頭のprint
すらも吐かないので、どうやらimport
の時点で問題が起こっているようです。なので、実際に入れた以下のモジュール群を順々に魔女狩りしていく。
import json
import pprint
import csv
import cmath
import math
import numpy as np
import copy
import os
結果的にはnumpy
が原因でした。幸いにも、numpyが必要な部分はプログラムで使わなかったので(np.pi
を使っていたが必要なくなった)このままモジュールを削除。もしnumpyを使ってゴリゴリに計算するプログラムをexe化しなければいけない場合は再度考えましょう……。
まとめ
「なんとかなったけど、ようわからんかった」という内容が多くて恐縮ですが、こういうのを積み重ねていかないとどんどん忘れてしまうので、自分のために書いておきました。今までこういうのをサボっていてふと不安になったので、これからはこういう日記みたいなプログラム記録も積極的に書いていこうと思います。
一応コード
完全に個人用のツールなので誰の役にも立たないと思いますが、一応。
import json
import pprint
import csv
import cmath
import math
import copy
import os
json_open = open('./sample.json', 'r')
j = json.load(json_open)
print("sample.jsonを読み込みました")
csv_file = open("./input.csv", "r",encoding="utf-8-sig")
c = csv.DictReader(csv_file)
points = []
for row in c:
points.append(row)
n = len(points)
for i in range(n-1):
new_j = copy.deepcopy(j["Movements"][0])
j["Movements"].append(new_j)
for i,p in enumerate(points):
x = float(p['X'])
y = float(p['Y'])
z = float(p['Z'])
duration = float(p['Duration'])
j["Movements"][i]["Duration"] = duration
j["Movements"][i]["StartPos"]["x"] = x
j["Movements"][i]["StartPos"]["y"] = y
j["Movements"][i]["StartPos"]["z"] = z
j["Movements"][i-1]["EndPos"]["x"] = x
j["Movements"][i-1]["EndPos"]["y"] = y
j["Movements"][i-1]["EndPos"]["z"] = z
y -= 1.5
c = complex(-x,-z)
rad = cmath.phase(c)
deg = -math.degrees(rad)+90
#print(deg)
j["Movements"][i]["StartRot"]["y"] = deg
j["Movements"][i-1]["EndRot"]["y"] = deg
c2 = complex(abs(c),y)
rad2 = cmath.phase(c2)
deg2 = math.degrees(rad2)
#print(-deg2)
j["Movements"][i]["StartRot"]["x"] = deg2
j["Movements"][i-1]["EndRot"]["x"] = deg2
pprint.pprint(j)
with open('output.json', 'w') as f:
json.dump(j, f, indent=4)
print("作業を完了しました")
end = input()