Python でテキストファイルやCSVファイルを読み込んで、その内容を画面に表示するプログラムを作る。このときファイルのエンコードを指定する必要がある。あわせて、セキュリティ対策として、「データが存在しない」「想定外のサイズ」「間違ったファイル/データ」対策を盛り込む。
サンプル・プログラム
テキストファイルを読み込む
Python でテキストファイルを読み込んで、その内容を画面に表示するプログラムを作ってみることにする。
プログラムは "readMemo.py" で、読み込むテキストファイル "readMemo.txt" を同じフォルダに配置してほしい。カレントディレクトリをこのフォルダにしたら、"readMemo1.py" を実行してみてほしい。
プログラムは "readMemo.py" で、読み込むテキストファイル "readMemo.txt" を同じフォルダに配置してほしい。カレントディレクトリをこのフォルダにしたら、"readMemo1.py" を実行してみてほしい。
# テキストファイル名
txtFileName = "./memo.txt"
# テキストファイルのエンコード = UTF-8
fileEncoding = "utf_8"
# 前処理:ファイルを開く
file = open(txtFilename, mode="rt", encoding=fileEncoding)
try:
# 本処理:ファイルを読み込んで変数に代入する.
contents = file.read()
finally:
# 後処理:ファイルを閉じる
file.close()
print(contents)
プログラム "readMemo.py" を見てほしい。
まず、読み込むテキストファイル名を変数 txtFileName に代入する。テキストファイル名なら何でもいい。別のフォルダにあるテキストファイルに変更してみてほしい。
次に、テキストファイルのエンコードを変数 fileEncoding に代入する。テキストファイル "readMemo.txt" は UTF-8 で作ってあるので、そのとおり代入する。
このエンコードの指定は重要で、Python は内部的に文字列を UTF-8 で処理しているため、正しいエンコードを与えてファイルを読み込まないと、プログラムが誤動作する恐れがある。これもセキュリティ対策の一環だ。
Python で利用できる主なエンコードを下表に示す。
まず、読み込むテキストファイル名を変数 txtFileName に代入する。テキストファイル名なら何でもいい。別のフォルダにあるテキストファイルに変更してみてほしい。
次に、テキストファイルのエンコードを変数 fileEncoding に代入する。テキストファイル "readMemo.txt" は UTF-8 で作ってあるので、そのとおり代入する。
このエンコードの指定は重要で、Python は内部的に文字列を UTF-8 で処理しているため、正しいエンコードを与えてファイルを読み込まないと、プログラムが誤動作する恐れがある。これもセキュリティ対策の一環だ。
Python で利用できる主なエンコードを下表に示す。
エンコード | 文字コード | 備考 |
---|---|---|
utf_8 | UTF-8 | インターネット上で最も普及している文字コード |
utf_16 | UTF-16 | UTF-8を固定長にした文字コード |
shift_jis | シフトJIS | 日本語 |
ascii | ASCII | 英数字線用の初期のコンピュータ用文字コード |
latin_1 | Latin-1 ISO-8859-1 | 欧米の言語 |
ファイルの読み込みは、前処理、本処理、後処理の3段階の流れになる。
まず前処理だが、組み込み関数 open を使ってターゲットのファイルを開く。第1引数はファイル名。第2引数はオープンモードで、"rt" はテキストファイルを読むという意味である。この関数は fileオブジェクトを返す。
本処理は、fileオブジェクトの readメソッドを使い、テキストファイルの内容を丸ごと変数 contents に代入する。
本処理には try文を用い、本処理が終わったら後処理を finallyブロックに記述する。ここでは、close メソッドを使ってファイルを閉じるだけでよい。
まず前処理だが、組み込み関数 open を使ってターゲットのファイルを開く。第1引数はファイル名。第2引数はオープンモードで、"rt" はテキストファイルを読むという意味である。この関数は fileオブジェクトを返す。
本処理は、fileオブジェクトの readメソッドを使い、テキストファイルの内容を丸ごと変数 contents に代入する。
本処理には try文を用い、本処理が終わったら後処理を finallyブロックに記述する。ここでは、close メソッドを使ってファイルを閉じるだけでよい。
ここで、後処理(close)を忘れると、対象ファイルが Pythonプログラムに握られたままになり、他のプログラムから編集できなくなってしまう。この問題を改善したプログラムが "readMemo2.py" である。
# テキストファイル名
txtFileName = "./memo.txt"
# テキストファイルのエンコード = UTF-8
fileEncoding = "utf_8"
# ファイルを読み込む:with文を利用
with open(txtFileName, "rt", encoding=fileEncoding) as file:
contents = file.read()
# ファイルの内容を表示する
"readMemo2.py" の実行結果は "readMemo1.py" と同じだが、[try文]がなくなり、代わりに with文が登場する。
width文 には、あらかじめ、ファイル操作の前処理、本処理、後処理が組み込まれており、これ1文を書くだけで済む。凡ミスを防ぐ意味で、ファイル処理には width文を使った方がいいだろう。
width文 には、あらかじめ、ファイル操作の前処理、本処理、後処理が組み込まれており、これ1文を書くだけで済む。凡ミスを防ぐ意味で、ファイル処理には width文を使った方がいいだろう。
ファイルが見つからないときなどの処理
「7.1 セキュリティ対策」の一覧表No.3に、データが存在しないときのセキュリティ対策を書いた。
変数 txtFileName に代入したファイルが見つからない場合、"readMemo2.py" はエラーを表示して止まるだけだが、プログラムの途中でファイルを読み込む場合にはそういうわけにはいかない。ファイルの存在チェックをして、必要な例外処理を書いておく必要がある。
"readMemo3.py" を実行してみてほしい。
変数 txtFileName に代入したファイルが見つからない場合、"readMemo2.py" はエラーを表示して止まるだけだが、プログラムの途中でファイルを読み込む場合にはそういうわけにはいかない。ファイルの存在チェックをして、必要な例外処理を書いておく必要がある。
"readMemo3.py" を実行してみてほしい。
# テキストファイル名
txtFileName = "./nofile.txt"
# テキストファイルのエンコード = UTF-8
fileEncoding = "utf_8"
# ファイルを読み込む
try:
with open(txtFileName, "rt", encoding=fileEncoding) as file:
contents = file.read()
# ファイルが存在しない
except FileNotFoundError:
print(f"'{txtFileName}'が存在しません.")
# その他の例外処理
except Exception as errmsg:
print(f"ファイル読み込みでエラーが発生しました -- {errmsg}")
# ファイルの内容を表示する
else:
print(contents)
open関数は、指定したファイルが見つからないときに FileNotFoundError例外を発生するので、これを try文でキャッチアップする。
また、これ以外の例外もキャッチアップしている。じつは、画像などのバイナリファイルを開こうとすると、エンコード処理で例外が発生するので、これをキャッチアップすることで、同時にセキュリティ対策一覧表No.6の「間違ったファイル/データ」を読み込むことを防止できる。
また、これ以外の例外もキャッチアップしている。じつは、画像などのバイナリファイルを開こうとすると、エンコード処理で例外が発生するので、これをキャッチアップすることで、同時にセキュリティ対策一覧表No.6の「間違ったファイル/データ」を読み込むことを防止できる。
import os
# 読み込み可能なファイルの最大サイズ
MAX_FILESIZE = 10 * 1024 * 1024
# テキストファイル名
txtFileName = "./memo.txt"
# テキストファイルのエンコード = UTF-8
fileEncoding = "utf_8"
try:
# ファイル最大サイズをチェックする
if (os.path.getsize(txtFileName) > MAX_FILESIZE):
raise OSError(f"ファイルサイズが{MAX_FILESIZE}バイトを超えています.")
# ファイルを読み込む
else:
with open(txtFileName, "rt", encoding=fileEncoding) as file:
contents = file.read()
# ファイルが存在しない
except FileNotFoundError:
print(f"'{txtFileName}'が存在しません.")
# その他の例外処理
except Exception as errmsg:
print(f"ファイル読み込みでエラーが発生しました -- {errmsg}")
# ファイルの内容を表示する
else:
print(contents)
ただ、画像や動画ファイルを除外できたからといって、誤って巨大なテキストファイルを指定してしまうかもしれない。そこで、セキュリティ対策一覧表No.5の「想定外のサイズ」のときに例外処理するようにしたプログラムが "readMemo4.py" である。
ファイルサイズを調べるには、OSモジュールにある os.path.getsize関数を利用する。ファイルサイズがバイト単位で返ってくるので、あらかじめ用意した最大値 MAX_FILESIZE(ここでは10Mバイトを代入)と比較して、超えていれば OSError例外を発生させる。
この例外は、最後の except Exception as errmsg でキャッチアップする。
ファイルサイズを調べるには、OSモジュールにある os.path.getsize関数を利用する。ファイルサイズがバイト単位で返ってくるので、あらかじめ用意した最大値 MAX_FILESIZE(ここでは10Mバイトを代入)と比較して、超えていれば OSError例外を発生させる。
この例外は、最後の except Exception as errmsg でキャッチアップする。
CSVファイルを読み込む
「5.5 タプル、集合型、辞書型」で作ったクラス全員の点数合計や科目別平均点を計算し、表形式に整形して画面に出力するプログラムでプログラムに埋め込んでいたデータを、ExcelからエクスポートしたCSVファイルを読み込む形に改良する。
プログラムは "testScore6.py" で、読み込むCSVファイルは "testScore.csv" を同じフォルダに配置してほしい。カレントディレクトリをこのフォルダにしたら、"testScore6.py" を実行してみてほしい。
プログラムは "testScore6.py" で、読み込むCSVファイルは "testScore.csv" を同じフォルダに配置してほしい。カレントディレクトリをこのフォルダにしたら、"testScore6.py" を実行してみてほしい。
import os
import csv
# 読み込み可能なファイルの最大サイズ
MAX_FILESIZE = 10 * 1024 * 1024
# CSVファイルのエンコード = シフトJIS
csvEncoding = "shift_jis"
# 成績表:CSVファイル名
csvFileName = "./testScore.csv"
try:
# ファイル最大サイズをチェックする
if (os.path.getsize(csvFileName) > MAX_FILESIZE):
raise OSError(f"ファイルサイズが{MAX_FILESIZE}バイトを超えています.")
# ファイルを読み込む
else:
rowNum = 1;
scores = []
with open(csvFileName, encoding = csvEncoding) as file:
for values in csv.reader(file, delimiter = ","):
# 1行目をキーとしてタプルに代入する
if (rowNum == 1):
keys = tuple(values)
print(keys)
# 2行目以降は辞書リストに入れる
else:
record = { key: (int(value) if value.isdigit() else value) for key, value in zip(keys, values) }
scores.append(record)
rowNum += 1
Python にはCSVファイルを処理するための標準モジュール csv が用意されており、まず、これを import して利用できるようにする。
ExcelからエクスポートしたCSVファイルは、デフォルト・エンコードがシフトJISである。変数 csvEncoding に shift_jis 代入しておく。もしUTF-8でエクスポートしたのであれば、utf_8 を代入する。
CSVファイル名は変数 csvFileName に代入しておく。
ファイルの最大サイズチェックや with文を使ったファイル操作は "readMemo4.py" と同じだ。今回は、readメソッドの代わりに、csvモジュールの reader関数を使う。
戻り値は、1つ1つのカラムの値を要素に代入したリストであり、前に作成した "testScore5.py" で、splitメソッドを使ってカラムに分解したときのリスト values と全く同じ値が入ることになる。これ以降の処理の流れだが、"readMemo4.py" でつくったファイル関係の例外処理が間に入っているが、elseブロック以降は "testScore5.py" と同じだ。
ExcelからエクスポートしたCSVファイルは、デフォルト・エンコードがシフトJISである。変数 csvEncoding に shift_jis 代入しておく。もしUTF-8でエクスポートしたのであれば、utf_8 を代入する。
CSVファイル名は変数 csvFileName に代入しておく。
ファイルの最大サイズチェックや with文を使ったファイル操作は "readMemo4.py" と同じだ。今回は、readメソッドの代わりに、csvモジュールの reader関数を使う。
csv.reader(CSVファイル名, delimiter = デリミタ)のようにして使う。デリミタとは、カラムを区切る文字のことで、通常はカンマ , を指定すればよい。
戻り値は、1つ1つのカラムの値を要素に代入したリストであり、前に作成した "testScore5.py" で、splitメソッドを使ってカラムに分解したときのリスト values と全く同じ値が入ることになる。これ以降の処理の流れだが、"readMemo4.py" でつくったファイル関係の例外処理が間に入っているが、elseブロック以降は "testScore5.py" と同じだ。
練習問題
次回予告
Python で、セキュリティ対策一覧表にある「悪意のあるスクリプト」「想定外の値」の対策をするために、文字列のバリデーションを行う。UNIX/Linuxに昔から備わっており、現在は多くのプログラミング言語で利用できる正規表現という仕組みを使うことで、さまざまなバリデーションを組むことができる。
次回は、正規表現を使った文字列バリデーションについて学ぶ。
次回は、正規表現を使った文字列バリデーションについて学ぶ。
コラム:テキストファイルは1行ずつ読むか?
C言語のfgets関数、BASIC言語のINPUT#命令は、テキストファイルから1行ずつデータを読み込む。
当時のOSの考え方として、キーボード入力とファイル入力を同じ「入力」としてアプリケーション・プログラムに提供していた。この考え方自体は現在も変わらないのだが、今回紹介した readメソッドのように、ファイルについては、一気にメモリに読み込む方式が主流だ。
当時のOSの考え方として、キーボード入力とファイル入力を同じ「入力」としてアプリケーション・プログラムに提供していた。この考え方自体は現在も変わらないのだが、今回紹介した readメソッドのように、ファイルについては、一気にメモリに読み込む方式が主流だ。
これには、メモリ容量が40年前に比べて桁違いに大きくなり、加えて仮想記憶も使えるようになったので、テラバイト級の極端に大きなテキストファイルでもないかぎり、一気にメモリに読み込めるようになったという背景がある。
もう1つ大切なことは、1行ずつ読み込んで処理をすると、処理中、他のアプリケーションが当該ファイルにアクセスできなくなる可能性があるためだ。クラウドサービスが当たり前になった現在、1人のユーザーがファイルを独占する状況は避けなければならない。
また、クラウドサービスでなくローカルドライブにあるファイルだったとしても、たとえばウイルス対策ソフトが非定期に当該ファイルを検疫するなど、マルチタスクで動いており、やはりファイルの独占はよろしくない。
そういう意味でも、ファイルはメモリに一気に読み込み、すぐに開放(close)してやるのがいい。これはファイルに書き込むときにも同じことが言える。
もう1つ大切なことは、1行ずつ読み込んで処理をすると、処理中、他のアプリケーションが当該ファイルにアクセスできなくなる可能性があるためだ。クラウドサービスが当たり前になった現在、1人のユーザーがファイルを独占する状況は避けなければならない。
また、クラウドサービスでなくローカルドライブにあるファイルだったとしても、たとえばウイルス対策ソフトが非定期に当該ファイルを検疫するなど、マルチタスクで動いており、やはりファイルの独占はよろしくない。
そういう意味でも、ファイルはメモリに一気に読み込み、すぐに開放(close)してやるのがいい。これはファイルに書き込むときにも同じことが言える。
(この項おわり)