Simple File I/O ¶

2025-06-06

主に以下の機能が含まれます。

    • fopen(): ファイルを開くために使用します。ファイル名とアクセスモード(読み込み、書き込み、追記など)を指定します。
      • 例: fid = fopen("data.txt", "r"); (data.txt を読み込みモードで開く)
    • fclose(): 開いたファイルを閉じるために使用します。これにより、リソースが解放され、データが確実にディスクに書き込まれます。
      • 例: fclose(fid);
  1. テキストデータの読み書き (Reading and Writing Text Data)

    • fgetl(): ファイルから1行ずつテキストを読み込みます。改行文字は含まれません。
    • fgets(): ファイルから1行ずつテキストを読み込みます。改行文字が含まれます。
    • fscanf(): フォーマット指定子(C言語のscanfに似ています)を使って、ファイルからフォーマットされたデータを読み込みます。
    • fprintf(): フォーマット指定子(C言語のprintfに似ています)を使って、ファイルにフォーマットされたデータを書き込みます。
      • 例: fprintf(fid, "名前: %s, 年齢: %d\n", "山田", 30);
  2. バイナリデータの読み書き (Reading and Writing Binary Data)

    • fread(): バイナリファイルから指定された数の要素を読み込みます。データ型を指定できます。
    • fwrite(): バイナリファイルに指定されたデータを書き込みます。
  3. ファイルポインタの操作 (File Pointer Manipulation)

    • frewind(): ファイルポインタをファイルの先頭に戻します。
    • fseek(): ファイルポインタを指定した位置に移動させます。
    • ftell(): 現在のファイルポインタの位置を取得します。

なぜ重要なのか?

  • 大規模なデータの処理
    メモリに一度にロードできないような大きなデータセットでも、ファイルから少しずつ読み込んで処理することができます。
  • 外部データとの連携
    他のプログラムやシステムが生成したデータを読み込んだり、Octaveで処理した結果を他のプログラムで利用できるように書き出したりできます。
  • データの永続化
    プログラムが終了してもデータを保存しておくことができます。


ファイルが見つからない (File Not Found Error)

  • トラブルシューティング
    • ls (Linux/macOS) または dir (Windows) コマンドで、ファイルが実際に存在するか、正確なファイル名を確認します。
    • ファイルのフルパス(絶対パス)を指定してみます。
      • 例: fid = fopen("/home/user/data/input.txt", "r"); (Linux/macOS)
      • 例: fid = fopen("C:\\Users\\user\\Documents\\data.txt", "r"); (Windows - バックスラッシュは2つ重ねるか、スラッシュを使う)
    • cd コマンドを使って、ファイルがあるディレクトリに移動してから実行します。
    • pwd コマンドで現在の作業ディレクトリを確認し、そこにファイルがあることを確認します。
  • 原因
    指定されたファイル名が存在しないか、パスが間違っています。
    • スペルミス: ファイル名や拡張子に誤りがある。
    • パスの問題: Octaveがファイルを検索するカレントディレクトリ以外の場所にファイルがある。
    • 大文字小文字の区別: オペレーティングシステムによっては、ファイル名の大文字小文字を区別します(例: Linux)。

アクセス権限の問題 (Permission Denied Error)

  • トラブルシューティング
    • ファイルのプロパティ(Windows)または ls -l (Linux/macOS) で、ファイルのアクセス権限を確認します。
    • 書き込みの場合は、そのディレクトリに書き込み権限があるか確認します。
    • 必要であれば、ファイルの権限を変更するか、書き込み可能な別の場所にファイルを保存します。
    • Windowsでは、Octaveを「管理者として実行」する必要がある場合もありますが、これは通常推奨されません。
  • 原因
    ファイルを読み書きするための適切な権限がありません。
    • 読み取り専用ファイルに書き込もうとしている。
    • 書き込み禁止のディレクトリにファイルを保存しようとしている。
    • 管理者権限が必要な場所にファイルを操作しようとしている。

ファイルが既に開かれている/使用中 (File Already Open/In Use)

  • トラブルシューティング
    • そのファイルを開いている可能性のある他のアプリケーションを閉じます。
    • Octave内で以前に開いたファイルを閉じ忘れていないか確認します。fclose(fid); を適切に呼び出しているか確認してください。
    • Octaveを再起動してみるのも一つの手です。
  • 原因
    別のプログラムやOctaveの別のセッションで、そのファイルが既に開かれているため、アクセスできません。

fopen のモード指定の誤り (Incorrect fopen Mode)

  • トラブルシューティング
    • fopen のモードを再確認し、意図した操作(読み込み、書き込み、追記など)に合ったモードを指定します。
      • "r": 読み込み(ファイルが存在しないとエラー)
      • "w": 書き込み(ファイルが存在すれば内容を消して新規作成、存在しなければ新規作成)
      • "a": 追記(ファイルが存在すれば末尾に追加、存在しなければ新規作成)
      • "r+": 読み書き(ファイルが存在しないとエラー)
      • "w+": 読み書き(ファイルが存在すれば内容を消して新規作成、存在しなければ新規作成)
      • "a+": 読み書き追記(ファイルが存在すれば末尾に追加、存在しなければ新規作成)
  • 原因
    fopen で指定したアクセスモードが、行おうとしている操作と一致していません。
    • 例: fopen("file.txt", "r") (読み込みモード) で開いたファイルに fprintf で書き込もうとしている。
    • 例: fopen("file.txt", "w") (書き込みモード - ファイルが存在すれば上書き) で開いた後、以前のデータを読み込もうとしている。

fscanf / fprintf のフォーマット文字列の不一致 (Mismatched Format Strings)

  • トラブルソーシング
    • ファイルのデータ形式を正確に把握し、それに対応するフォーマット指定子を使用します。
    • 数値(整数、浮動小数点数)を扱う場合は %d, %f, %e などを適切に使い分けます。
    • 文字列を扱う場合は %s を使用します。
    • デバッグのために、読み込んだデータを一時的に表示して確認します。
  • 原因
    fscanf でファイルを読み込む際、ファイルの実際のデータ形式とフォーマット指定子(例: %d, %f, %s)が一致していない。または、fprintf で書き込む際に、データ型とフォーマット指定子が一致していない。
    • 例: 数字が書かれている行を %s (文字列) で読み込もうとする、またはその逆。
    • 例: fprintf(fid, "%d", 3.14); (浮動小数点数を整数として書き込もうとする)

ファイル終端の扱い (feof)

  • トラブルシューティング
    • ファイルからデータを読み込む関数(fscanffread)の戻り値(読み込んだ要素数など)を利用して、データが正常に読み込まれたかどうかを判断する方が確実です。
    • 例: [data, count] = fscanf(fid, '%f', [columns, Inf]); のように、count をチェックすることで、実際に読み込んだ要素数を把握できます。
  • 原因
    while ~feof(fid) のようなループでファイル終端をチェックする場合、最後のデータが読み込まれた直後には feoftrue にならないことがあります。freadfscanf が実際にデータの読み込みに失敗してから feoftrue になります。そのため、最後のデータが重複して処理されたり、予期せぬエラーが発生したりすることがあります。

メモリ不足 (Out of Memory)

  • トラブルシューティング
    • ファイルをチャンク(小さな塊)に分割して読み込み、処理する。
    • 必要のないデータをメモリから解放する (clear コマンド)。
    • Octaveの64ビット版を使用しているか確認する(使用可能なメモリ量が増える)。
  • 原因
    非常に大きなファイルを一度にメモリに読み込もうとすると、メモリが不足してエラーが発生する場合があります。

fclose の忘れ (Forgetting to Close Files)

  • トラブルシューティング
    • ファイルを開いたら、必ず対応する fclose(fid); を呼び出すようにします。
    • スクリプトの最後に fclose('all'); を入れて、開いているすべてのファイルを閉じることができます(デバッグ時やスクリプトの終わりに便利ですが、特定のファイルだけを閉じる場合は推奨されません)。
    • 堅牢なコードを書く場合は、try-catch-finally ブロックを使用して、エラーが発生した場合でも必ずファイルを閉じるようにします。
  • 原因
    fopen で開いたファイルを fclose で閉じ忘れると、リソースリークやデータがディスクに正しく書き込まれない(バッファリングされている場合)といった問題が発生する可能性があります。特に書き込みモードで開いた場合、fclose しないとデータが失われることがあります。


テキストファイルへの書き込み (Writing to a Text File)

最も基本的なのは、テキストデータをファイルに書き込むことです。

% ファイル名
filename = "my_text_data.txt";

% ファイルを書き込みモードで開く ('w' は書き込みモード、ファイルが既存なら上書きされる)
% fid はファイル識別子 (File IDentifier) で、以降のファイル操作でこのIDを使う
fid = fopen(filename, "w");

% ファイルが正常に開かれたか確認
if fid == -1
    disp("Error: Could not open the file for writing.");
else
    % ファイルに文字列を書き込む (fprintf は C言語の printf に似ている)
    fprintf(fid, "Hello, Octave!\n");
    fprintf(fid, "This is a simple example.\n");

    % 変数の内容を書き込む
    data_value = 123.45;
    text_string = "Example data";
    fprintf(fid, "Data value: %f\n", data_value);
    fprintf(fid, "Text string: %s\n", text_string);

    % 数値の行列を書き込む (例: CSV形式)
    matrix_data = [1 2 3; 4 5 6; 7 8 9];
    % 行ごとにループして書き込む
    for i = 1:rows(matrix_data)
        fprintf(fid, "%d,%d,%d\n", matrix_data(i,:)); % 各要素をカンマ区切りで書き込み
    end

    % ファイルを閉じる (重要!)
    fclose(fid);
    disp(["Data written to ", filename]);
end

解説

  • fclose(fid);: 開いたファイルを閉じます。これは非常に重要です。 ファイルを閉じないと、書き込みが完了しなかったり、ファイルが他のプログラムからアクセスできなくなったりする可能性があります。
  • fprintf(fid, "...", ...);: ファイルにフォーマットされた文字列や変数の内容を書き込みます。
    • 第一引数: ファイル識別子 fid
    • 第二引数以降: C言語の printf と同じように、フォーマット文字列と出力する変数を指定します。\n は改行コードです。
  • if fid == -1: fopen が失敗した場合(例:アクセス権がない場合など)は、fid が -1 を返します。エラーハンドリングは重要です。
  • fid = fopen(filename, "w");: fopen 関数でファイルを開きます。
    • 第一引数: ファイル名。
    • 第二引数: オープンモード。"w" は「書き込みモード」を意味し、もし my_text_data.txt が既に存在すればその内容をすべて消して上書きし、存在しなければ新規作成します。
  • filename = "my_text_data.txt";: 書き込むファイルの名前を定義します。

実行後、my_text_data.txt というファイルが作成され、以下のような内容が書き込まれています。

Hello, Octave!
This is a simple example.
Data value: 123.450000
Text string: Example data
1,2,3
4,5,6
7,8,9

テキストファイルからの読み込み (Reading from a Text File)

先ほど書き込んだ my_text_data.txt を読み込んでみましょう。

% ファイル名
filename = "my_text_data.txt";

% ファイルを読み込みモードで開く ('r' は読み込みモード)
fid = fopen(filename, "r");

% ファイルが正常に開かれたか確認
if fid == -1
    disp("Error: Could not open the file for reading.");
else
    disp(["Reading data from ", filename, ":"]);

    % 1行ずつ読み込む (feof はファイル終端に達したかを確認)
    while !feof(fid)
        % fgetl は改行文字を含まないで1行読み込む
        line = fgetl(fid);
        if ischar(line) % 読み込みが成功したか確認
            disp(line);
        end
    end

    % ファイルポインタを先頭に戻す (fscanf で再度読み込むために)
    frewind(fid);

    % フォーマットを指定してデータを読み込む (fscanf)
    % この例では、最後の3行の数値データを読み込むことを想定
    % %d,%d,%d\n に合わせて読み込む
    disp("Reading structured data (matrix):");
    read_matrix = [];
    while !feof(fid)
        % 最後の3行が数値データだと仮定して、そこまで読み飛ばす
        line = fgetl(fid);
        if ischar(line) && regexp(line, '^\d+,\d+,\d+$') % 数字とカンマで構成される行を検出
            [values, count] = sscanf(line, "%d,%d,%d"); % 文字列からフォーマットされたデータを読み込む
            if count == 3
                read_matrix = [read_matrix; values']; % 行列に追加 (行ベクトルにするため転置)
            end
        end
    end
    disp("Read matrix:");
    disp(read_matrix);

    % ファイルを閉じる (重要!)
    fclose(fid);
end

解説

  • read_matrix = [read_matrix; values'];: 読み込んだ行(values)を行列 read_matrix に追加していきます。values' は列ベクトルをsscanfが返すため、行ベクトルに転置して追加します。
  • [values, count] = sscanf(line, "%d,%d,%d");: sscanf は文字列から指定されたフォーマットでデータを抽出します。ここでは、カンマ区切りの整数を読み込んでいます。
    • values: 抽出されたデータ。
    • count: 正常に読み込まれた要素の数。
  • frewind(fid);: ファイルポインタをファイルの先頭に戻します。これにより、一度読み込みを行った後でも、再度ファイルの最初から読み込むことができます。
  • ischar(line): fgetl はファイルの終わりに達したりエラーが発生したりすると、文字列ではない値を返す可能性があります。そのため、ischar で読み込みが成功したかを確認するのが安全です。
  • line = fgetl(fid);: ファイルから1行ずつテキストを読み込みます。fgetl は改行文字を含まないため、純粋な行の内容が得られます。
  • while !feof(fid): feof(fid) は、ファイル識別子 fid が指すファイルの終端に達している場合に true を返します。! で否定することで、ファイル終端に達するまでループを続けます。
  • fid = fopen(filename, "r");: ファイルを「読み込みモード」で開きます。ファイルが存在しない場合は -1 を返します。

数値データなどを効率的に保存したい場合や、特定のフォーマットのバイナリファイルを扱う場合は、バイナリI/Oを使用します。

% バイナリファイル名
binary_filename = "my_binary_data.bin";

% バイナリデータを準備 (例: 浮動小数点数の行列)
data_to_write = [1.1 2.2 3.3; 4.4 5.5 6.6; 7.7 8.8 9.9];

% ファイルをバイナリ書き込みモードで開く ('wb' はバイナリ書き込みモード)
fid = fopen(binary_filename, "wb");

if fid == -1
    disp("Error: Could not open the binary file for writing.");
else
    % fwrite でバイナリデータを書き込む
    % 第二引数は書き込むデータ、第三引数はデータ型
    % ここでは 'double' (倍精度浮動小数点数) として書き込む
    % Octave/MATLAB はデフォルトで列優先で書き込むため、行優先で書き込みたい場合は転置が必要な場合がある
    % 通常、配列をそのまま渡すと、列ごとに書き込まれます
    fwrite(fid, data_to_write, "double");
    fclose(fid);
    disp(["Binary data written to ", binary_filename]);
end

% バイナリファイルからの読み込み
fid = fopen(binary_filename, "rb"); % 'rb' はバイナリ読み込みモード

if fid == -1
    disp("Error: Could not open the binary file for reading.");
else
    % fread でバイナリデータを読み込む
    % 第二引数: 読み込む要素数 (ここでは全ての要素), または読み込む行列のサイズ [行数, 列数]
    % 第三引数: 読み込むデータ型 (書き込んだ時と同じ型を指定)
    % Octave/MATLAB の fread はデフォルトで列優先で読み込むため、
    % もとの行列のサイズが分かっている場合は [rows, cols] を指定すると良い
    % もし行数、列数が不明で、元の行列の形を復元したい場合は、
    % 読み込んだ後に reshape などで形を整える
    [read_data, count] = fread(fid, size(data_to_write), "double");

    fclose(fid);
    disp("Binary data read from file:");
    disp(read_data);

    % 読み込んだデータは列ベクトルになるので、元の行列の形にreshapeで戻す
    if count == numel(data_to_write)
        read_data_reshaped = reshape(read_data, rows(data_to_write), columns(data_to_write));
        disp("Reshaped binary data:");
        disp(read_data_reshaped);
    end
end

解説

  • reshape(read_data, rows(data_to_write), columns(data_to_write));: 読み込んだ列ベクトル read_data を、元の行列の形 rows(data_to_write)columns(data_to_write) 列に整形し直します。
  • [read_data, count] = fread(fid, size(data_to_write), "double");:
    • 第二引数: 読み込む要素の総数、または読み込む行列のサイズ [rows, cols]。ここでは size(data_to_write) を指定して、元の行列と同じサイズで読み込もうとしています。
    • 第三引数: 読み込むデータの型。書き込んだ時と同じ型を指定する必要があります。
    • fread は、デフォルトでデータを列優先で読み込み、列ベクトルとして返します。
  • fwrite(fid, data_to_write, "double");:
    • 第一引数: ファイル識別子 fid
    • 第二引数: 書き込むデータ(行列でもベクトルでも可)。
    • 第三引数: 書き込むデータの型(例: "double", "single", "int8", "uint16" など)。この型でデータがディスクに保存されます。
  • fid = fopen(binary_filename, "wb");: "wb" は「バイナリ書き込みモード」を意味します。"b" をつけることで、バイナリモードでファイルを開くことを明示します(Windows環境では特に重要)。

これらの例は、Octaveにおける「Simple File I/O」の基本的な使い方を示しています。

  • バイナリI/O (fwrite, fread)
    数値データなどを効率的かつ正確に保存・読み込みたい場合に利用します。データ型を明示的に指定する必要があります。
  • テキストI/O (fprintf, fgetl, fscanf, sscanf)
    人間が読める形式のテキストデータを扱う場合に便利です。行単位やフォーマット指定子を使って読み書きできます。
  • fopen() と fclose()
    ファイル操作の開始と終了に必須です。特に fclose() は忘れずに実行することが重要です。


主な代替方法を以下に示します。

save と load 関数(Octave/MATLAB独自のバイナリ形式 .mat)

これはOctave(およびMATLAB)で最も一般的で便利なデータ保存・読み込み方法です。変数をそのままバイナリ形式(.mat ファイル)で保存し、後で簡単に読み込むことができます。

  • 用途
    Octaveで生成したデータや計算結果を、後でOctaveで再利用する場合に最適です。
  • 特徴
    • Octaveのデータ型(数値、文字列、セル配列、構造体など)をそのまま保存・復元できる。
    • 非常に高速。
    • データを圧縮して保存できるため、ファイルサイズが小さくなることがある。
    • 異なるOctave/MATLABのバージョン間である程度の互換性がある。
% データを準備
my_vector = [1 2 3 4 5];
my_matrix = magic(3);
my_string = "Hello, Octave data!";
my_struct.name = "Octave User";
my_struct.id = 123;

% データを .mat ファイルに保存
% 'data.mat' に my_vector と my_matrix を保存
save data.mat my_vector my_matrix;

% 全ての変数を保存 (現在のワークスペースの全て)
save all_data.mat;

% 全ての変数をASCII形式で保存(可読性はあるが精度が落ちる可能性、非推奨)
% save -ascii ascii_data.txt my_matrix;

% 既存の変数をクリア
clear my_vector my_matrix my_string my_struct;

% 保存したデータを読み込む
load data.mat; % my_vector と my_matrix がワークスペースにロードされる
disp("Loaded my_vector:");
disp(my_vector);
disp("Loaded my_matrix:");
disp(my_matrix);

load all_data.mat; % all_data.mat に保存された全ての変数がロードされる
disp("Loaded my_string:");
disp(my_string);
disp("Loaded my_struct.name:");
disp(my_struct.name);

dlmwrite と dlmread 関数(区切り文字付きテキストファイル)

区切り文字(カンマ、タブ、スペースなど)で区切られた数値データを扱うための便利な関数です。CSV (Comma Separated Values) ファイルの読み書きによく使われます。

  • 用途
    スプレッドシートソフトウェアや他のプログラミング言語とのデータ連携。
  • 特徴
    • CSVやTSV (Tab Separated Values) 形式のファイルに対応。
    • 他のソフトウェア(Excelなど)とのデータ交換に便利。
    • 数値データに特化。
% 書き込むデータ (数値行列)
data_to_csv = [10 20 30; 40 50 60; 70 80 90];
csv_filename = "my_data.csv";

% CSVファイルに書き込み (カンマ区切り)
dlmwrite(csv_filename, data_to_csv, ",");
disp(["Data written to ", csv_filename]);

% CSVファイルから読み込み
read_csv_data = dlmread(csv_filename, ",");
disp("Data read from CSV:");
disp(read_csv_data);

% スペース区切りで書き込み、読み込みの例
space_data = [1.1 2.2; 3.3 4.4];
space_filename = "my_data.txt";
dlmwrite(space_filename, space_data, " ");
read_space_data = dlmread(space_filename, " ");
disp("Data read from space-delimited file:");
disp(read_space_data);

csvread と csvwrite 関数(CSVファイル)

dlmread/dlmwrite のCSV専用版とも言えます。より単純なCSVファイル操作に適しています。

  • 用途
    単純なCSVデータの読み書き。
  • 特徴
    CSVファイルに特化しており、より直感的に使える。
% 書き込むデータ
data_for_csv = [100 200; 300 400];
csv_file_simple = "simple_data.csv";

% CSVファイルに書き込み
csvwrite(csv_file_simple, data_for_csv);
disp(["Data written to ", csv_file_simple]);

% CSVファイルから読み込み
read_simple_csv_data = csvread(csv_file_simple);
disp("Data read from simple CSV:");
disp(read_simple_csv_data);

textread と textscan 関数(複雑なテキストファイル)

fscanf よりも柔軟で強力なテキスト解析関数です。不規則なフォーマットのテキストファイルからデータを抽出するのに非常に役立ちます。

  • 用途
    ログファイル、科学データ、設定ファイルなど、構造が複雑なテキストファイルの解析。
  • 特徴
    • 複数の異なるデータ型を同時に読み込める。
    • コメント行のスキップ、ヘッダー行のスキップ、特定の区切り文字の指定など、詳細な制御が可能。
    • 正規表現を使ったパターンマッチングと組み合わせることも可能。
% 複雑なテキストファイルを作成
fid = fopen("complex_data.txt", "w");
fprintf(fid, "Header Line 1\n");
fprintf(fid, "Header Line 2\n");
fprintf(fid, "# This is a comment\n");
fprintf(fid, "Name: Alice, Age: 30, Score: 95.5\n");
fprintf(fid, "Name: Bob, Age: 25, Score: 88.0\n");
fprintf(fid, "Name: Carol, Age: 35, Score: 92.1\n");
fclose(fid);

% textscan を使って読み込み
% %s -> 文字列, %d -> 整数, %f -> 浮動小数点数
% 'Delimiter', ',' -> カンマ区切り
% 'HeaderLines', 2 -> 最初の2行をスキップ
% 'CommentStyle', '#' -> # で始まる行をコメントとしてスキップ
fid = fopen("complex_data.txt", "r");
C = textscan(fid, "Name: %s, Age: %d, Score: %f", "Delimiter", ",", "HeaderLines", 2, "CommentStyle", "#");
fclose(fid);

names = C{1};
ages = C{2};
scores = C{3};

disp("Read using textscan:");
disp("Names:"); disp(names);
disp("Ages:"); disp(ages);
disp("Scores:"); disp(scores);

高レベルなパッケージ/ツールボックス

Octave Forge(Octaveの公式パッケージ集)には、特定のファイル形式を扱うためのパッケージが存在します。

  • XML パッケージ
    XMLファイルのパースと生成をサポートします。
  • ODBC パッケージ
    データベース(ODBC経由)との接続を可能にします。
  • io パッケージ
    NetCDF, HDF5, FITS など、科学技術計算でよく使われるファイル形式の読み書きをサポートします。

これらのパッケージは、特定の高度なファイル形式を扱う場合に、低レベルなファイルI/O関数よりもはるかに効率的で安全な方法を提供します。使用するには、まずパッケージをインストールし、ロードする必要があります。

% io パッケージのインストール例 (初回のみ)
% pkg install -forge io

% io パッケージのロード (Octave起動時に毎回必要)
pkg load io;

% 例: HDF5 ファイルの書き込み(io パッケージがロードされている前提)
% h5create("my_hdf5_data.h5", "/my_group/my_dataset", [10, 20]);
% h5write("my_hdf5_data.h5", "/my_group/my_dataset", rand(10, 20));
% data_from_h5 = h5read("my_hdf5_data.h5", "/my_group/my_dataset");
  • 特定の科学フォーマットやデータベース連携
    io パッケージや ODBC パッケージなど、適切なパッケージを検討。
  • 複雑なテキストファイル
    textscan が非常に強力。
  • 他のソフトウェアと連携
    CSV形式なら dlmwrite/dlmread または csvwrite/csvread
  • 最も簡単で推奨
    Octaveでしか使わないデータなら save/load (.mat)。