【Octave】textreadだけじゃない!テキストファイル読み込みの代替メソッドを比較解説

2025-06-06

textreadは、テキストファイルからデータを読み込むための関数です。主に、特定の形式(フォーマット)で記述されたデータを、変数に読み込む際に使用されます。

textreadの基本的な機能と特徴

  • 複数の出力変数
    読み込んだデータを複数の変数に分割して格納することができます。
  • ヘッダー行のスキップ
    ファイルの先頭にヘッダー行がある場合、headerlinesオプションでスキップする行数を指定できます。
  • 区切り文字の指定
    データがコンマやスペースなどの特定の区切り文字で区切られている場合、delimiterオプションを使ってその区切り文字を指定できます。
  • フォーマット指定
    読み込むデータの形式(例: 整数、浮動小数点数、文字列など)をフォーマット文字列で指定できます。これはC言語のscanf関数に似ています。例えば、%fは浮動小数点数、%dは整数、%sは文字列を表します。
  • ファイルからの読み込み
    textreadは、ファイル名を指定して、その内容を読み込みます。

textreadの書式(主なもの)

[a, b, ...] = textread(filename, format)
[a, b, ...] = textread(filename, format, n)
[a, b, ...] = textread(filename, format, prop1, value1, ...)
[a, b, ...] = textread(filename, format, n, prop1, value1, ...)
  • prop1, value1, ...: 追加のプロパティと値のペア。例: 'delimiter', ',' (区切り文字をカンマにする)、'headerlines', 1 (最初の1行をスキップする)
  • n (optional): フォーマット文字列を何回繰り返してデータを読み込むかを指定します。これにより、読み込む行数やデータ数を制限できます。
  • format: データをどのように読み込むかを指定するフォーマット文字列(例: "%f %s %d"
  • filename: 読み込むテキストファイルの名前(文字列)

簡単な使用例

例えば、以下のような内容のdata.txtというファイルがあるとします。

10.5,apple,100
20.1,banana,200
30.8,orange,300

これをOctaveで読み込むには、次のようにします。

[num, fruit, quantity] = textread('data.txt', '%f%s%d', 'delimiter', ',');

disp(num);
disp(fruit);
disp(quantity);

このコードを実行すると、numには浮動小数点数の配列、fruitには文字列のセル配列、quantityには整数の配列がそれぞれ読み込まれます。

注意点

textreadはOctave(およびMATLAB)で古くから使われている関数ですが、大規模なファイルや複雑なフォーマットのファイルを扱う場合には、より新しい関数であるtextscanの方が推奨されることがあります。textscantextreadよりも柔軟で、パフォーマンスも優れているとされています。



ファイルが見つからない (error: textread: file not found)

最も基本的なエラーです。指定されたファイルがOctaveの現在の作業ディレクトリ、または検索パス上に存在しない場合に発生します。

トラブルシューティング

  • スペルミス
    ファイル名にスペルミスがないか再確認してください。
  • 検索パスの確認
    path コマンドでOctaveの検索パスを確認し、ファイルのあるディレクトリが追加されているか確認します。必要であれば addpath で追加します。
  • 現在の作業ディレクトリの確認
    pwd コマンドで現在の作業ディレクトリを確認し、ファイルがそこにあるか確認します。もし違う場合は、cd コマンドでファイルのあるディレクトリに移動するか、ファイルのフルパスを指定してください。
  • ファイルパスの確認
    ファイル名が正しいか、拡張子を含めて確認してください。

フォーマット文字列とデータが一致しない (error: textread: number of output variables must match that specified by FORMAT)

textread のフォーマット文字列 (format 引数) が、実際にファイルに存在するデータの列数や型と一致しない場合に発生します。

トラブルシューティング

  • 空のフィールド
    • データが空のフィールドを含んでいる場合、'emptyvalue', NaN などのオプションを指定して、空のフィールドをどのように扱うかを明示的に指定すると良い場合があります。
  • データ型の不一致
    • 例: textread('data.txt', '%d'); で、ファイルに文字列や浮動小数点数が入っている場合。
    • ファイル内の実際のデータ型(整数、浮動小数点数、文字列など)と、フォーマット指定子(%d, %f, %s など)が合っているか確認してください。例えば、整数しかないはずの場所にテキストが混ざっている場合など。
  • フォーマット文字列と出力変数の数
    • 例: [a, b] = textread('data.txt', '%f %s');
    • ファイルに3列のデータがあるのに、フォーマット文字列が2つの変数しか指定していない場合や、出力変数の数がフォーマット文字列の数と合わない場合にこのエラーが出ます。ファイルの内容とフォーマット文字列、出力変数の数を一致させてください。

区切り文字の問題 (delimiter の不一致)

ファイル内のデータが、指定した区切り文字 (delimiter オプション) 以外で区切られている場合に、読み込みがうまくいかないことがあります。

トラブルシューティング

  • 複数の区切り文字
    複数の区切り文字がある場合や、区切り文字が連続している場合の挙動を確認します。デフォルトでは、連続する空白文字は1つの区切り文字として扱われますが、他の区切り文字ではそうではない場合があります。
  • 正しい区切り文字の指定
    ファイルがカンマ区切り (.csv) なら ','、タブ区切りなら '\t'、スペース区切りなら指定しないか ' ' を使用します。

ヘッダー行のスキップ忘れ (headerlines の問題)

ファイルにデータ以外のヘッダー行が含まれているにもかかわらず、headerlines オプションでスキップする行数を指定していない場合、ヘッダー行がデータとして解釈され、エラーや不正な結果を引き起こすことがあります。

トラブルシューティング

  • ヘッダー行の数の確認
    テキストファイルの先頭に何行ヘッダーがあるかを確認し、'headerlines', N (Nはスキップする行数) を正確に指定してください。

大容量ファイルの読み込み時のメモリ不足またはパフォーマンスの問題

textread は、ファイルを一度にメモリに読み込む実装になっているため、非常に大きなファイルを扱う場合にメモリ不足になったり、処理に時間がかかったりすることがあります。

トラブルシューティング

  • 部分的な読み込み
    必要であれば、fgetlfscanf などを使って、行ごとにデータを読み込むループを自分で記述することもできます。
  • load の使用
    もしファイルが純粋な数値データのみで構成されており、特定の区切り文字(スペースなど)で区切られているのであれば、load 関数が最も効率的で高速です。
    • 例: data = load('numeric_data.txt');
  • textscan の使用を検討
    Octave(およびMATLAB)では、textread の代わりに textscan の使用が推奨されています。textscan はファイルをチャンク(塊)に分けて読み込むことができ、メモリ効率が良く、大規模なファイル処理に適しています。
    • textscan を使う場合は、fopen でファイルを開き、fclose で閉じる必要があります。
    • 例:
      fid = fopen('large_data.txt', 'r');
      C = textscan(fid, '%f %s %d', 'delimiter', ',');
      fclose(fid);
      % C はセル配列として返されるので、必要に応じてデータを抽出
      num = C{1};
      fruit = C{2};
      quantity = C{3};
      

文字列内の空白の扱い (whitespace の問題)

%s フォーマット指定子で文字列を読み込む際に、文字列内に含まれる空白が区切り文字として解釈されてしまい、期待通りの文字列が読み込めない場合があります。

トラブルシューティング

  • 引用符付き文字列
    ファイル内の文字列が引用符で囲まれている場合(例: "Hello World")、%q フォーマット指定子を使用すると、引用符を無視して文字列を読み込めます。
  • whitespace オプションの変更
    textread のデフォルトでは、スペースやタブなどが空白文字として扱われ、%s で読み込む際に区切り文字となります。文字列内の空白を保持したい場合は、'whitespace', '' のように空の文字列を指定して、空白を区切り文字として扱わないようにします。

Octaveのバージョンによる互換性

textread はOctaveのバージョンによって挙動が微妙に異なる場合や、一部のオプションがサポートされていない場合があります。特に古いバージョンを使用している場合は注意が必要です。

  • 最新バージョンへの更新
    可能な限り、最新のOctaveバージョンを使用することで、バグ修正や機能改善の恩恵を受けられます。
  • Octaveのドキュメントを確認
    使用しているOctaveのバージョンに対応する textread のドキュメント(help textread またはオンラインドキュメント)を参照し、利用可能なオプションや書式を確認してください。


例1: 最も基本的な数値データの読み込み

スペースで区切られた数値データのみのファイルを読み込む最もシンプルな例です。

data_num.txt の内容

10 20 30
40 50 60

Octave コード

% data_num.txt から数値データを読み込む
% フォーマット文字列 '%f %f %f' は、3つの浮動小数点数を読み込むことを指定
% 複数の出力変数にそれぞれの列が格納されます

[col1, col2, col3] = textread('data_num.txt', '%f %f %f');

disp('--- 例1: 数値データの読み込み ---');
disp('列1:');
disp(col1);
disp('列2:');
disp(col2);
disp('列3:');
disp(col3);

% 結果:
% col1 = [10; 40]
% col2 = [20; 50]
% col3 = [30; 60]

例2: 異なるデータ型と区切り文字の指定

カンマ区切り(CSV形式)で、数値、文字列、整数の混在データを読み込む例です。

data_mixed.csv の内容

10.5,apple,100
20.1,banana,200
30.8,orange,300

Octave コード

% data_mixed.csv から混在データを読み込む
% フォーマット文字列: '%f' (浮動小数点数), '%s' (文字列), '%d' (整数)
% 'delimiter', ',' オプションで区切り文字がカンマであることを指定

[price, item, quantity] = textread('data_mixed.csv', '%f%s%d', 'delimiter', ',');

disp('--- 例2: 異なるデータ型と区切り文字 ---');
disp('価格 (price):');
disp(price);
disp('商品 (item):');
disp(item);
disp('数量 (quantity):');
disp(quantity);

% 結果:
% price = [10.5; 20.1; 30.8]
% item = {"apple"; "banana"; "orange"} (セル配列として格納される)
% quantity = [100; 200; 300]

例3: ヘッダー行のスキップ

ファイルにデータ以外のヘッダー行が含まれている場合の例です。

data_with_header.txt の内容

Header Line 1
Header Line 2
Value1,Value2,Value3
1,A,10
2,B,20

Octave コード

% data_with_header.txt からヘッダー行をスキップしてデータを読み込む
% 'headerlines', 2 オプションで最初の2行をスキップすることを指定

[id, name, score] = textread('data_with_header.txt', '%d%s%d', ...
                              'delimiter', ',', ...
                              'headerlines', 3); % "Value1,Value2,Value3" もスキップする場合

disp('--- 例3: ヘッダー行のスキップ ---');
disp('ID:');
disp(id);
disp('名前 (name):');
disp(name);
disp('スコア (score):');
disp(score);

% 結果:
% id = [1; 2]
% name = {"A"; "B"}
% score = [10; 20]


上記の例では、'headerlines', 3 とすることで、Value1,Value2,Value3 の行もスキップしています。もしこの行をデータとして読み込みたい場合は、'headerlines', 2 とし、その行のフォーマットを適切に指定する必要があります。

例4: 特定の行数のみを読み込む

ファイルの先頭から特定の行数(またはデータセット数)だけを読み込む例です。

data_large.txt の内容

100 101
102 103
104 105
106 107
108 109

Octave コード

% data_large.txt から最初の3行のみを読み込む
% 3 は、フォーマット文字列 '%f %f' を3回繰り返す(つまり3行読み込む)ことを意味します

[val1, val2] = textread('data_large.txt', '%f %f', 3);

disp('--- 例4: 特定の行数のみを読み込む ---');
disp('値1 (val1):');
disp(val1);
disp('値2 (val2):');
disp(val2);

% 結果:
% val1 = [100; 102; 104]
% val2 = [101; 103; 105]

例5: 文字列内の空白を保持する

文字列内に空白が含まれており、それを一つの文字列として読み込みたい場合の例です。

data_phrases.txt の内容

ID1,Hello World,Category A
ID2,Open Octave,Category B

Octave コード

% data_phrases.txt から文字列内の空白を保持して読み込む
% '%q' は引用符で囲まれた文字列(または空白を含まない単語)を読み込む
% 'whitespace', '' オプションは、空白文字を区切り文字として扱わないようにする

% 注意: '%q' は通常、引用符で囲まれた文字列に最も適しています。
% 引用符がない場合は、'whitespace', '' と '%s' を組み合わせることが考えられますが、
% その場合、フィールド全体が1つの文字列として読み込まれるため、
% 他の区切り文字(カンマなど)を正確に指定することが重要です。

% ここでは、カンマ区切りで、真ん中のフィールドに空白を含む文字列があるケースを想定します。
% Octaveのtextreadでは、デフォルトで'%s'は空白で区切られてしまいます。
% このような場合は、一般的にtextscanの方が柔軟です。
% しかし、textreadで実現しようとすると、少し工夫が必要です。

% 例として、"Hello World" のようなフィールド全体を読み込むには、
% Octaveのtextreadでは直接的な方法が限られます。
% もし `"Hello World"` のように引用符で囲まれていれば `%q` が使えますが、
% 引用符がない場合は、textreadの限界にぶつかることがあります。

% 以下のコードは、もしファイルが `"Hello World"` のように引用符で囲まれていれば機能します。
% `data_phrases_quoted.txt` の内容:
% ID1,"Hello World",Category A
% ID2,"Open Octave",Category B

% [id_q, phrase_q, category_q] = textread('data_phrases_quoted.txt', '%s%q%s', 'delimiter', ',');
% disp('--- 例5a: 引用符付き文字列の読み込み (%q) ---');
% disp('ID:'); disp(id_q);
% disp('フレーズ:'); disp(phrase_q);
% disp('カテゴリ:'); disp(category_q);

% 引用符がない場合の一般的な解決策は、textscan を使うことです。
% textreadで、文字列内の空白を一つのフィールドとして読み込むのは難しい場面が多いです。
% (もしテキストが純粋にカンマで区切られていて、フィールド内に空白が含まれる場合は、
% textreadはそれを個別のフィールドとして解釈してしまう可能性があります。)

% ただし、`textread` は `'whitespace'` オプションも持ちます。
% `data_phrases.txt` の内容が上記のように引用符なしの場合、
% 空白を区切り文字と見なさないようにすることは可能ですが、
% その場合、`textread`はカンマを区切り文字として認識しなくなります。

% よって、このようなケースは `textscan` の使用が強く推奨されます。
% 参考までに `textscan` の例を以下に示します。
disp('--- 例5: 文字列内の空白を保持 (textreadでは困難なためtextscanを推奨) ---');
disp('この種のデータには textread よりも textscan が適しています。');
disp('以下は textscan の例です (ファイルはdata_phrases.txtを使用):');

fid = fopen('data_phrases.txt', 'r');
if fid == -1
    error('ファイルが開けません: data_phrases.txt');
end

% textscanでは、カンマを区切り文字として指定し、%sで文字列を読み込みます。
% 空白の扱いについては、'CollectOutput', true などで調整しますが、
% デフォルトでもカンマ区切りであれば内部の空白は保持されます。
C = textscan(fid, '%s%s%s', 'delimiter', ',');
fclose(fid);

id_ts = C{1};
phrase_ts = C{2};
category_ts = C{3};

disp('ID (textscan):');
disp(id_ts);
disp('フレーズ (textscan):');
disp(phrase_ts);
disp('カテゴリ (textscan):');
disp(category_ts);

% 結果 (textscan):
% phrase_ts = {"Hello World"; "Open Octave"}

例6: コメント行の処理 (Octave textread には直接的なオプションはない)

textread には、ファイル内のコメント行をスキップするための直接的なオプションはありません。しかし、ファイルを開いて行ごとに読み込み、コメント行をスキップするロジックを自分で実装することで対応できます。これは通常、textscanfgetl と組み合わせる方が効率的です。

data_with_comments.txt の内容

# This is a comment line
100,abc,1.1
# Another comment
200,def,2.2
disp('--- 例6: コメント行の処理 (textreadでは直接対応不可、textscan+fgetl推奨) ---');
disp('textreadにはコメント行をスキップする直接的なオプションはありません。');
disp('この場合は、textscan や fgetl を使って自分で処理する必要があります。');

filename = 'data_with_comments.txt';
fid = fopen(filename, 'r');
if fid == -1
    error('ファイルが開けません: %s', filename);
end

data = {}; % 読み込んだデータを格納するセル配列
while ~feof(fid)
    line = fgetl(fid); % 1行読み込む
    if ischar(line) && ~isempty(line) && line(1) ~= '#' % コメント行と空行をスキップ
        % textscanで1行分のデータを解析
        % textscanはセル配列を返すので、C{:}で中身を取り出す
        C = textscan(line, '%d%s%f', 'delimiter', ',');
        if ~isempty(C{1}) % 読み込みに成功した場合のみ追加
             data = [data; {C{1}(1), C{2}{1}, C{3}(1)}]; % 読み込んだ値を格納
        end
    end
end
fclose(fid);

% 読み込んだデータを適切な変数に分割
id_c = cell2mat(data(:,1));
name_c = data(:,2);
value_c = cell2mat(data(:,3));

disp('ID (手動スキップ):');
disp(id_c);
disp('名前 (手動スキップ):');
disp(name_c);
disp('値 (手動スキップ):');
disp(value_c);

% 結果:
% id_c = [100; 200]
% name_c = {"abc"; "def"}
% value_c = [1.1; 2.2]


textscan 関数

textscan は、textread の最も強力で推奨される代替方法です。特に、大規模なファイル、複雑なフォーマット、またはより細かい制御が必要な場合に優れています。

特徴

  • 出力はセル配列
    デフォルトでは、各列がセル配列の要素として返されます。これは、異なるデータ型の列を扱う場合に非常に便利です。
  • ファイル識別子 (fid) を使用
    fopen でファイルを開き、fclose で閉じる必要があります。これにより、より細かいファイル制御が可能です。
  • チャンク読み込み
    ファイル全体を一度にメモリに読み込むのではなく、必要なブロック(チャンク)ごとに読み込むことができます。これにより、大規模なファイルの処理におけるメモリ効率が向上します。
  • より柔軟なフォーマット指定
    textread と同様に %f, %d, %s などのフォーマット指定子を使用しますが、さらに多くのオプション(例: CollectOutputEmptyValueCommentStyle など)を提供します。

基本的な書式

fid = fopen(filename, 'r');
C = textscan(fid, format, N, prop1, value1, ...);
fclose(fid);

使用例
data_complex.txt の内容:

# Data starts below
ID,Name,Value
1,Alice,10.5
2,Bob,20.1
3,Charlie,30.8

Octave コード

filename = 'data_complex.txt';
fid = fopen(filename, 'r');

if fid == -1
    error('ファイルが開けません: %s', filename);
end

% ヘッダー行とコメント行をスキップ
% 'headerlines', 1 は # Data starts below をスキップ
% textscan には 'CommentStyle' オプションもありますが、OctaveのtextscanはMATLABほど強力ではありません。
% この例では、手動でヘッダーを読み飛ばすのが確実です。
fgetl(fid); % "# Data starts below" をスキップ
fgetl(fid); % "ID,Name,Value" をスキップ

% データを読み込む
% '%d,%s,%f' は「整数,文字列,浮動小数点数」のカンマ区切りを指定
% 'Delimiter', ',' は区切り文字がカンマであることを明示
% 'CollectOutput', true は、同じ型の複数の出力変数をまとめて1つの行列にするオプション(ここでは使用しない)
C = textscan(fid, '%d,%s,%f', 'Delimiter', ',');

fclose(fid);

% 読み込んだデータを個々の変数に割り当てる
id = C{1};
name = C{2};
value = C{3};

disp('--- textscan の例 ---');
disp('ID:');
disp(id);
disp('名前:');
disp(name);
disp('値:');
disp(value);

load 関数

load 関数は、主に数値データのみで構成されたファイルや、Octave/MATLAB独自のバイナリ形式のファイルを読み込むのに最適です。非常に高速でシンプルです。

特徴

  • 単一の行列として読み込み
    ファイル全体が1つの行列として読み込まれます。
  • シンプルな書式
    フォーマット文字列を指定する必要がありません。
  • 非常に高速
    テキスト解析のオーバーヘッドが少ないため、大規模な数値データを高速に読み込めます。
  • 数値データに特化
    スペース区切りまたはタブ区切りの純粋な数値データを読み込むのに最適です。

基本的な書式

M = load(filename);

使用例
data_pure_numbers.txt の内容:

1 2 3
4 5 6
7 8 9

Octave コード

% data_pure_numbers.txt から数値データを読み込む
% ファイル全体が1つの行列として変数 `data` に格納されます

data = load('data_pure_numbers.txt');

disp('--- load 関数の例 ---');
disp('読み込まれた行列:');
disp(data);

% 結果:
% data =
%    1   2   3
%    4   5   6
%    7   8   9

低レベルファイルI/O関数 (fopen, fgetl, fscanf, fclose など)

最も柔軟な方法ですが、コードが複雑になる傾向があります。ファイルの内容を1行ずつ、あるいは特定の形式で読み込み、自分で解析ロジックを記述できます。非常に特殊なファイル形式や、行ごとに条件分岐が必要な場合に有効です。

特徴

  • エラーハンドリング
    fopen の戻り値を確認するなど、詳細なエラーハンドリングが可能です。
  • コメント行のスキップや条件付き読み込み
    fgetl で1行ずつ読み込み、特定のパターンに基づいてスキップしたり、解析したりできます。
  • 最高の柔軟性
    ファイルの読み込みプロセスを完全に制御できます。

基本的な書式

fid = fopen(filename, 'r');
if fid == -1
    % エラー処理
end

% 繰り返し読み込み
while ~feof(fid) % ファイルの終端に達するまで
    line = fgetl(fid); % 1行読み込む
    if ischar(line) % 文字列として読み込めたか確認
        % 読み込んだ行を処理するロジック (例: strsplit, sscanf, regexprep などを使用)
    end
end

fclose(fid);

使用例
data_log.txt の内容:

# Log file started at 2025-06-06
INFO: Event A occurred. Value: 10.5
WARN: Something is wrong. Value: 20.1
# End of log
ERROR: Critical failure! Value: 30.8
filename = 'data_log.txt';
fid = fopen(filename, 'r');

if fid == -1
    error('ファイルが開けません: %s', filename);
end

data_values = []; % 抽出した値を格納する配列
log_messages = {}; % ログメッセージを格納するセル配列

while ~feof(fid) % ファイルの終端に達するまで
    line = fgetl(fid); % 1行読み込む

    if ischar(line) && ~isempty(line) && line(1) ~= '#' % 文字列で、空でなく、コメント行ではない
        % 特定のパターンを検索 (例: "Value: " の後に続く数値)
        [~, ~, ~, ~, ~, ~, split_parts] = regexp(line, 'Value: ([\d.]+)', 'once');
        if ~isempty(split_parts) % パターンが見つかった場合
            value_str = split_parts{1}{1};
            data_values = [data_values; str2double(value_str)];
            log_messages = [log_messages; line]; % ログメッセージ全体を保存
        end
    end
end

fclose(fid);

disp('--- 低レベルI/O関数の例 ---');
disp('抽出された値:');
disp(data_values);
disp('対応するログメッセージ:');
disp(log_messages);
  • 低レベルファイルI/O (fopen, fgetl, fscanf など)
    最高の柔軟性を提供しますが、コードが複雑になります。非常に特殊な要件や、行ごとの詳細な制御が必要な場合にのみ使用します。
  • load
    純粋な数値データのみのファイルを高速に読み込む場合に最適です。
  • textscan
    textread の上位互換であり、より柔軟なフォーマット指定とチャンク読み込みが可能です。ほとんどのテキストファイル読み込みにおいて推奨される方法です。
  • textread
    シンプルなフォーマットのテキストファイルを素早く読み込むのに便利ですが、大規模ファイルや複雑なケースには限界があります。