プログラミング学習 Day 5:Programming-File

今回は、ファイルのI/Oを説明します。まずは、PASCALからです。

■PASCAL File Read/ Write

例題を見てみましょう。

これは、インプットファイルからデータを一行ごとに読み出して

そのデータをアウトプットファイルに書き出します。

ファイルのデータがStringの時は、ClassでTstringListを使います。

文字列の可変長配列(行一覧)としてファイルの読み書き、文字列の連結/分割などに利用します。

TStringListインスタンスなので Create して使い、最後に Free する必要があります。

Methods

  • Count: Integer — 要素数
  • Strings[index] / Lines[index](index は 0-origin) — 個々の行にアクセス
  • Add(s: string): Integer — 末尾に追加(戻り値は追加位置)
  • Insert(index, s) — 指定位置に挿入
  • Delete(index) — 指定要素を削除
  • IndexOf(s): Integer — 要素のインデックス(見つからなければ -1)
  • Clear — 全削除
  • LoadFromFile(filename) / SaveToFile(filename) — ファイル入出力
  • Text: string — 全体を一つの文字列(行区切りは sLineBreak)
  • CommaText, DelimitedText / Delimiter / StrictDelimiter — 区切りテキスト操作
  • Sort / CustomSort — ソート
  • AddObject(s, obj) / Objects[i] — 文字列にオブジェクトを紐付け可能
  • Assign(OtherList) / AddStrings(OtherList) — 別の TStringList の全要素をコピー

PASCAL

var

  sl: TStringList;

  i: Integer;

begin

  sl := TStringList.Create;

  try

    sl.LoadFromFile(‘input.txt’);      // ファイルを行単位で読み込む

    for i := 0 to sl.Count – 1 do

      sl[i] := Trim(sl[i]);           

    s:=’AAAAAAAAAA’;

    sl.Add(s);

    sl.SaveToFile(‘output.txt’);       // 結果をファイルに保存

  finally

    sl.Free;

  end;

end.テキスト

■Python Read, write

これを、Pythonで書いてみます。

読み込み

# ファイル全体を文字列で読む

with open(‘input.txt’, ‘r’, encoding=’utf-8′) as f:

    text = f.read()

#一行ずつ読み印刷

with open(‘input.txt’, ‘r’, encoding=’utf-8′) as f:

    for line in f:          

        line = line.rstrip(‘\n’)

        print(line)

#メモリーにリスト構造に全部読み込む

with open(‘input.txt’, ‘r’, encoding=’utf-8′) as f:

    first = f.readline()

    all_lines = f.readlines()   # メモリに全行をリストで一気に読み込む

書き込み

# 上書き(ファイルがなければ作成)

with open(‘output.txt’, ‘w’, encoding=’utf-8′) as f:

    f.write(‘Hi masa \n’)

# 追加

with open(‘output.txt’, ‘a’, encoding=’utf-8′) as f:

    f.write(‘追加行\n’)

# 複数行を一括書き込み

lines = [‘a\n’, ‘b\n’, ‘c\n’]

with open(‘out.txt’, ‘w’, encoding=’utf-8′) as f:

    f.writelines(lines)

■Python CSV, Json

CSVやJsonファイルの扱い方は以下の通り。

Python

import csv, json

# CSV 読み書き
with open('data.csv', 'r', encoding='utf-8', newline='') as f:
reader = csv.reader(f)
for row in reader:
print(row)

with open('out.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(['a','b','c'])
writer.writerows([[1,2,3],[4,5,6]])

# JSON
obj = {'a':1, 'b':2}
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(obj, f, ensure_ascii=False, indent=2)

with open('data.json', 'r', encoding='utf-8') as f:
obj2 = json.load(f)

■Python Stack save, load

Python 
StackをFileに書き込み、読み出す例(JSONを使う)

# json_stack.py
import json
from collections import deque
from typing import Deque, Any

def save_stack_json(path: str, stack: Deque[Any]) -> None:
# stack: deque([...]) with top at right (append/pop)
with open(path, "w", encoding="utf-8") as f:
# convert to list for JSON (left..right)
json.dump(list(stack), f, ensure_ascii=False)

def load_stack_json(path: str) -> Deque[Any]:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
return deque(data)

# example
if __name__ == "__main__":
s = deque([1, 2, 3]) # 3 is top
save_stack_json("stack.json", s)
s2 = load_stack_json("stack.json")
print(s2) # deque([1,2,3])

■Python Pickle

Python
StackやQueueのデータ構造をそのままファイルに保存できる方法として
Pickleがあります。以下はその例です。


# pickle_queue.py
import pickle
from collections import deque
from typing import Deque, Any

def save_pickle(path: str, obj: Any) -> None:
with open(path, "wb") as f:
pickle.dump(obj, f, protocol=pickle.HIGHEST_PROTOCOL)

def load_pickle(path: str) -> Any:
with open(path, "rb") as f:
return pickle.load(f)

if __name__ == "__main__":
q = deque(["a", {"x": 1}, 3])
save_pickle("queue.pickle", q)
q2 = load_pickle("queue.pickle")
 # deque([...])
print(q2)

■PASCAL


固定サイズレコードを連続書き込みするPASCALの方法(バイナリ)を紹介します。
StackのPush、Popを使います。
SaveStackToFileLoadStackFromFile
でファイルに書き込み、読出しをします。

program StackExample;
{$mode objfpc}{$H+}
uses
SysUtils, Classes;

type
// 固定長レコード
 TItem = record
id: Integer;
name: array[0..31] of Char; // NUL 終端を自分で扱う
{ 動的配列を使ったスタック実装(トップは High(stack)) }
type
TItemStack = array of TItem;

procedure Push(var S: TItemStack; const It: TItem);
begin
SetLength(S, Length(S) + 1);
S[High(S)] := It;
end;

function Pop(var S: TItemStack; out It: TItem): Boolean;
begin
if Length(S) = 0 then
begin
Result := False;
Exit;
end;
It := S[High(S)];
SetLength(S, Length(S) - 1);
Result := True;
end;

{ ファイルへ保存:全内容をバイナリで上書き(tmp -> rename ) }
procedure SaveStackToFile(const FileName: string; const S: TItemStack);
var
tmpName: string;
f: File of TItem;
i: Integer;
begin
tmpName := FileName + '.tmp';
AssignFile(f, tmpName);
Rewrite(f);
try
for i := 0 to High(S) do
Write(f, S[i]);
finally
CloseFile(f);
end;
// atomic replace
if FileExists(FileName) then
SysUtils.DeleteFile(FileName);
SysUtils.RenameFile(tmpName, FileName);
end;

{ ファイルから読み出してスタックを復元(読み込んだ順序が配列の 0..N-1) }
procedure LoadStackFromFile(const FileName: string; var S: TItemStack);
var
f: File of TItem;
it: TItem;
begin
S := nil;
if not FileExists(FileName) then Exit;
AssignFile(f, FileName);
Reset(f);
try
while not Eof(f) do
begin
Read(f, it);
Push(S, it);

end;
finally
CloseFile(f);
end;
end;

{ ユーティリティ:文字列を固定長 Char 配列にコピー(NUL で埋める) }
procedure SetName(var It: TItem; const S: string);
var
i, L, MaxLen: Integer;
buf: array[0..31] of Char;
begin
MaxLen := Length(buf);
FillChar(buf, SizeOf(buf), 0);
L := Length(S);
if L > MaxLen then L := MaxLen;
for i := 1 to L do
buf[i-1] := S[i];
It.name := buf;
end;

{ デバッグ表示 }
procedure DumpStack(const S: TItemStack);
var
i: Integer;
begin
Writeln('Stack len=', Length(S));
for i := 0 to High(S) do
Writeln(Format('[%d] id=%d name=%s', [i, S[i].id, string(S[i].name)]));
end;

var
stack: TItemStack;
item: TItem;
fname: string = 'stack.dat';
ok: Boolean;
begin
{ サンプル作成 }
stack := nil;
item.id := 1; SetName(item, 'masa'); Push(stack, item);
item.id := 2; SetName(item, 'Yamada'); Push(stack, item);
item.id := 3; SetName(item, 'Joe'); Push(stack, item);

Writeln('Before save:');
DumpStack(stack);

SaveStackToFile(fname, stack);

{ 読み直しテスト }
LoadStackFromFile(fname, stack);

Writeln('After load:');
DumpStack(stack);

{ Pop の例 }
ok := Pop(stack, item);
if ok then
Writeln('Popped: id=', item.id, ' name=', string(item.name))
else
Writeln('Stack empty');

{ 保存して終了 }
SaveStackToFile(fname, stack);
end.

■参考


ExtractFileName(fname) は SysUtils ユニットにある関数で、パス文字列からファイル名(拡張子を含むファイル部分)を取り出します。
ChangeFileExt は ファイル名の拡張子を置き換したり削除したりするために使います。
PASCAL

program ReadFromFile_Write;
{$mode objfpc}{$H+}
uses
SysUtils, Classes;

procedure ReadLinesFromFile(const fname: string; out lines: TStringList);
begin
lines := TStringList.Create;
lines.LoadFromFile(fname);
end;

//main
var
fname, outfname: string;
lines, outtxt: TStringList;
i: Integer;
begin
if ParamCount >= 1 then
fname := ParamStr(1)
else
fname := 'testf.txt';

if not FileExists(fname) then
begin
Writeln('File not found: ', fname);
Exit;
end;

outfname := ChangeFileExt(ExtractFileName(fname), '') + '_out.txt';

outtxt := TStringList.Create;
lines := nil;
try
ReadLinesFromFile(fname, lines);

outtxt.Add(Format('Read %d line(s). :', [lines.Count]));
for i := 0 to lines.Count - 1 do
begin
outtxt.Add(Format('%d: %s', [i + 1, lines[i]]));
end;
outtxt.Add('');

{ Save results to output file }
outtxt.SaveToFile(outfname);
Writeln('Wrote output to: ', outfname);
finally
if Assigned(lines) then lines.Free;
outtxt.Free;
end;
end.

著者:松尾正信
株式会社京都テキストラボ代表取締役