挨拶
今回もPythonの学習を進めていきます。今回作るのはCSVファイルの結合ツールを作成していきます。
前回記事:クリップボード履歴管理ツール
実行環境
- OS : Pop!_OS
- Anaconda
教本
今回の目標
今回の最終目標は複数のCSVファイルを選択し、結合してテーブルに表示するツールの作成です。
CSVファイルについて
まずは、今回の学習に当たってCSVファイルについての概要を確認します。
CSVファイルとは
- 多くの場合、CSVの1行目は"ヘッダー(項目名)" である。しかし、中には1行目がヘッダーでは無いCSVファイルもあるため確認が必要
- ヘッダーがある場合、2行目以降のカンマ( , )で区切られた部分が "フィールド(データ値)"と呼ばれる
- CSVの行は1行が"1件のデータ(レコード)" を表し、複数のフィールドを持つ
CSV例
果物,産地,価格
りんご,青森,"300"
桃,山梨,"600"
Pythonには、CSVファイルを読み書きするライブラリがありますが、今回は標準ライブラリのcsvモジュールを使います。
CSVファイルへの読み込みと書き込み
まずはツール作成前に、PythonでCSVファイルの読み込みと書き込みをする方法を確認します。細かい処理の内容については、コード内のコメントを参考にしてください。
CSVファイルの作成
まずは、読み込むためのCSVファイルを作成します。
今回は、新しいファイルを作成後し、前述のCSV例をコピペして、CSVファイルとして保存しました。このファイルを使い、読み込みと書き込みの確認を行います。
作成したCSVファイルを読み込む
ここから、CSVファイルをCSVモジュールなしで読み込むコードを書いていきます。
CSVファイル読み込みコード(CSVモジュールなし)
# 1.CSVデータをテキストとして読み込みカンマで区切る - 不完全な方法
result = [] # 読み込んだデータを格納する配列resultを作成
with open("test.csv","r",encoding= "utf-8") as fp: # CSVファイルをテキストとして読み込み
for line in fp.readlines(): # ファイルの各行を順番に読み込み
row = line.split(",") # カンマで区切り配列rowに格納
result.append(row) # 配列resultにrowを追加
# データが正しく読み込めたか確認
peach = result[2] # CSVファイル3行目のデータを取得
print(f"(1) 果物: {peach[0] } 価格: {peach[2]}") # index0:果物名とindex2:価格を表示
結果:# 結果:(1) 果物: 桃 価格: "600"
コメントに"不完全な方法"と書いた理由は、この方法だと、データ内にダブルクオートが含まれていた場合、ダブルクオートも一緒に出力されてしまいます。 ダブルクオートは本来、囲んだ部分を文字列として認識させるためにあり、表示されるのは望ましくありません。
また、CSVのデータにカンマが含まれていた場合、その部分で区切りを付けてしまうため正しく表示できません。
データにカンマが含まれていた場合の表示:# 結果:(1) 果物: 桃 価格: "6,
CSVファイル読み込みコード(モジュールあり)
今度は、標準ライブラリのCSVモジュールを使い、同じ処理のコードを書いていきます。
# 2.CSVモジュールを使いCSVを読み込む
import csv # CSVモジュールをインポート
result = [] # 読み込んだデータを格納する配列resultを作成
with open("test.csv","r",encoding= "utf-8") as fp: # CSVファイルをテキストとして読み込み
csv_reader = csv.reader(fp) # CSVモジュールの"reader"を使ったオブジェクトを作成
for row in csv_reader:
result.append(row)
# データが正しく読み込めたか確認
peach = result[2] # CSVファイル3行目のデータを取得
print(f"(1) 果物: {peach[0] } 価格: {peach[2]}") # index0:果物名とindex2:価格を表示
#結果:(2) 果物: 桃 価格: 600
1と2のコードで使ったCSVデータは同じものを使用していますが、1では価格の表示が"600"となっていたのに対し、2ではダブルクオートが抜かれ、600と表示されています。CSVモジュールを使うことで、望ましい結果で出力できました。
CSVファイルに書き込む
今度は、PythonからCSVファイルを作成しデータを書き込むコードを書いていきます。
import csv
# リスト型のデータを作成。この値がCSVファイルに書き込まれる
data = [
["ABC,Inc", 500000,4 ],
["WXZT,LLC",800000,4]
]
# CSV形式のファイルを作成し書き込む
with open("company.csv","w",encoding="utf-8") as f:
writer = csv.writer(f) # CSVファイルの書き込みオブジェクトを作成
writer.writerows(data) # 作成したファイルにdataのデータを全て格納
作成したCSVの中身:
ABC,Inc 500000 4
WXZT,LLC 800000 4
Excelとの互換について
CSVファイルをExcelで開く際に起こる文字化けですが、文字エンコードの違いが原因となることが多いです。
しかし、私の環境にExcelを導入していないため検証できないこと、学習の目的が"CSVファイルの結合ツール作成"であることから、今回は説明を省きます。
CSVファイルを読み込んでダイアログ表示させる
先程作成したCSVファイルを読み込んで、ファイルを選択したこと、保存したことを表示するダイアログを表示させるコードを書いていきます。
# ダイアログを画面に表示させるコード
import TkEasyGUI as sg
# ファイル選択のダイアログを表示
file = sg.popup_get_file(
"CSVファイルを選択してください。",
multiple_files= False, # 単一のファイルの場合はFalseを指定
file_types=("CSVファイル","*.csv")
)
sg.popup(file,"*.csv") # 選択したファイルの絶対パスを表示
# ファイル保存のダイアログ
file = sg.popup_get_file(
"保存するCSVファイルを選択してください",
save_as=True,
file_types=(("CSVファイル",".*csv"),)
)
sg.popup(file,title="保存したファイル名")
このコードではダイアログの表示しか行えないため、ファイルの保存はできません。このコードをベースに、複数のファイルを選択して結合するツールを作成していきます。
複数のCSVファイルを選択して結合するツール
このツールでできることは、
- 複数のCSVファイルを選択
- 選択したファイルを結合してテーブル形式で表示
- 「保存」ボタンを押すことで結合したCSVデータをファイルに保存
GUIには、前回同様に"TkEasyGUI"を使用します。
# ファイル結合するツール
import csv
import TkEasyGUI as sg
# ツールの基本動作を定義するmain関数を作成
def main():
while True:
file = sg.popup_get_file(
"CSVファイルを選択してください",
multiple_files=True, # 複数選択したいときはTrueを指定
file_types=(("CSV","*.csv"),)
)
if len(file) ==0 or file == "": #選択したCSVファイルが存在しない場合は処理を中止
break
all_datas = []
for filenames in file: #選択したファイルを変数に格納
data = read_csv(filenames) #read_csv関数を使いcsvファイルを読み込む
if data is None:
sg.popup_error(filenames + "ファイルが読み込めません")
continue
# ヘッダーが重複する場合1つに統一
if len(all_datas) >= 2 and len(data) >=2:
if all_datas[0] == data[0]:
data = data[1:]
all_datas += data
if show_csv(all_datas) == False: # show_csv関数を使いテーブル表示する
break
#read_csv関数を作成
def read_csv(filenames):
try:
with open(filenames,"r",encoding="utf-8") as f:
reader = csv.reader(f)
data = [row for row in reader] #ファイルの内容を1行ずつ読み配列dataに格納
return data
except:
pass
return None #読み込みに市パイした場合は何も返さない
#CSVをテーブル表示するshow_csv関数を作成
def show_csv(data):
if len(data) == 0: # 中身がない場合は空と表示する
data = [["空"],["空"]]
#表示するレイアウトを指定
layout = [
[sg.Table(
key="-data-",
values=data[1:],#データ(フィールド)を指定
headings=data[0], # ヘッダー部分を指定
expand_x=True,expand_y=True ,#テーブルの表示幅をウィンドウの大きさに合わせる
justification="left" ,# 左詰めで表示
auto_size_columns=True, #データに合わせてカラムの大きさを変える
max_col_width=30, #カラムの最大値を指定
font=("Aria",14))],
[sg.Button("ファイルを選択"),sg.Button("保存"),sg.Button("終了")]
]
# ウィンドウを作成
window = sg.Window("CSV結合ツール",layout,size=(500,300),resizable=True)
#イベントごとの処理
flag = False
while True:
event,_ = window.read()
if event in [sg.WINDOW_CLOSED,"終了"]:
break
#ファイル選択を選んだ場合フラグを変更
if event == "ファイルを選択":
flag = True
break
#ファイルの保存を選択した場合CSVファイルに書き込む
if event == "保存":
filenames = sg.popup_get_file(
"保存するファイルを選択",
save_as=True,
file_types=(("CSV","*.csv"),)
)
if filenames == "" or filenames is None:
continue
with open(filenames,"w",encoding="utf-8",newline="") as f:
writer = csv.writer(f)
writer.writerows(data)
window.close()
return flag
if __name__ == "__main__":
main()
結果
果物 産地 価格
りんご 青森 300
桃 山梨 600
りんご 青森 300
桃 山梨 600
これで完成です。
結果を見ての通り、結合したことにより"りんご","青森","300","桃","山梨","600"の各データが2回ずつCSVファイルに出力されました。
感想
何度かPythonの学習をして来ましたが、一番厄介に感じるのは"インデント"です。
javaやc言語などは、ブロックごとに分けてあるため、インデントでのエラーは発生しません。しかしPythonはインデントがこのブロックに相当するため、インデントにミスがあると、前のインデントから順を追う必要が出てきて大変です。
慣れるまでは時間がかかりそうですが、やれば覚えるの精神で続けて行きます。