Main Content

本页翻译不是最新的。点击此处可查看最新英文版本。

使用低级 I/O 导入文本数据文件

概述

通过低级文件 I/O 函数,可以最大程度地控制文件数据的读取和写入。但是,相对于更易于使用的高级函数,例如 importdata,这些低级函数要求指定更为详细的文件信息。有关读取文本文件的高级函数的详细信息,请参阅导入文本文件

如果高级函数无法导入数据,请使用下列函数之一:

  • fscanf,读取文本或 ASCII 文件(即可以在文本编辑器中查看的文件)中的格式化数据。有关详细信息,请参阅读取格式化模式的数据

  • fgetlfgets,一次读取文件中的一行,其中每一行通过换行符分隔。有关详细信息,请参阅逐行读取数据

  • fread,读取从相应字节或位级开始的数据流。有关详细信息,请参阅通过低级 I/O 导入二进制数据

有关其他信息,请参阅:

注意

低级文件 I/O 函数基于 ANSI® 标准 C 库中的函数。但是,MATLAB® 包括向量化版本的函数,通过最少的控制循环在数组中读取和写入数据。

读取格式化模式的数据

要导入 importdatatextscan 无法读取的文本文件,可考虑使用 fscanffscanf 函数要求对文件的格式进行描述,但包括用于描述格式的众多选项。

例如,按如下所示创建文本文件 mymeas.datmymeas.dat 中的数据包括重复的时间、日期和测量数据集。标题文本包括测量数据集数 N

Measurement Data
N=3

12:00:00
01-Jan-1977
4.21  6.55  6.78  6.55
9.15  0.35  7.57  NaN
7.92  8.49  7.43  7.06
9.59  9.33  3.92  0.31
09:10:02
23-Aug-1990
2.76  6.94  4.38  1.86
0.46  3.17  NaN   4.89
0.97  9.50  7.65  4.45
8.23  0.34  7.95  6.46
15:03:40
15-Apr-2003
7.09  6.55  9.59  7.51
7.54  1.62  3.40  2.55
NaN   1.19  5.85  5.05
6.79  4.98  2.23  6.99

打开文件

与任何低级 I/O 函数一样,在读取前,使用 fopen 打开文件,并获取文件标识符。默认情况下,fopen 使用 'r' 权限打开文件进行读取访问。

处理完文件后,使用 fclose(fid) 关闭文件。

描述数据

使用格式设定符描述文件中的数据,例如 '%s' 表示文本,'%d' 表示整数,或者 '%f' 表示浮点数。(有关设定符的完整列表,请参阅 fscanf 参考页。)

要跳过文件中的字面字符,请在格式说明中包括这些字符。要跳过数据字段,请在设定符中使用星号 ('*')。

例如,mymeas.dat 的标题行如下所示:

Measurement Data   % skip the first 2 words, go to next line:  %*s %*s\n
N=3                % ignore 'N=', read integer:  N=%d\n
                   % go to next line:  \n
12:00:00
01-Jan-1977
4.21  6.55  6.78  6.55
...

读取标题并返回 N 的值:

N = fscanf(fid, '%*s %*s\nN=%d\n\n', 1);

指定要读取的值数

默认情况下,fscanf 会重复应用您的格式描述,直至它无法匹配数据,或者它到达文件的末尾。

您也可以指定要读取的值数,这样 fscanf 不会尝试读取整个文件。例如,在 mymeas.dat 中,每个测量数据集包含固定数目的行和列:

measrows = 4;
meascols = 4;
meas  = fscanf(fid, '%f', [measrows, meascols])';

在工作区中创建变量

有多种方式可将 mymeas.dat 存储在 MATLAB 工作区中。在此例中,将值读取到一个结构体中。该结构体的每个元素都有三个字段:mtimemdatemeas

注意

fscanf 按列顺序使用数值填充数组。要使输出数组与文件中数值数据的方向匹配,请对数组进行转置操作。

filename = 'mymeas.dat';
measrows = 4;
meascols = 4;

% open the file
fid = fopen(filename);

% read the file headers, find N (one value)
N = fscanf(fid, '%*s %*s\nN=%d\n\n', 1);

% read each set of measurements
for n = 1:N
    mystruct(n).mtime = fscanf(fid, '%s', 1);
    mystruct(n).mdate = fscanf(fid, '%s', 1);

    % fscanf fills the array in column order,
    % so transpose the results
    mystruct(n).meas  = ...
      fscanf(fid, '%f', [measrows, meascols])';
end

% close the file
fclose(fid);

逐行读取数据

MATLAB 提供了下列两个函数分别用于读取文件中的行和将这些行存储为字符向量:fgetlfgetsfgets 函数会将数据行和换行符都复制到输出,但 fgetl 不会。

以下示例使用 fgetl 按一次读取一行的方式读取整个文件。函数 litcount 确定给定的字符序列 (literal) 是否将显示在每一行中。如果是,则该函数将输出整行,并在行前加上该文字在行中出现的次数。

function y = litcount(filename, literal)
% Count the number of times a given literal appears in each line.

fid = fopen(filename);
y = 0;
tline = fgetl(fid);
while ischar(tline)
   matches = strfind(tline, literal);
   num = length(matches);
   if num > 0
      y = y + num;
      fprintf(1,'%d:%s\n',num,tline);
   end
   tline = fgetl(fid);
end
fclose(fid);

创建一个名为 badpoem 的输入数据文件:

Oranges and lemons,
Pineapples and tea.
Orangutans and monkeys,
Dragonflys or fleas.

要确定 'an' 显示在此文件中的次数,请调用 litcount

litcount('badpoem','an')

这将返回:

2: Oranges and lemons,
1: Pineapples and tea.
3: Orangutans and monkeys,
ans =
     6

检测文件末尾 (EOF)

当一次读取数据的一部分时,可以使用 feof 检查是否已到达文件的末尾。当文件指针位于文件末尾时,feof 返回值 1。否则,将返回 0

注意

打开一个空文件不会将文件位置指示符移到文件末尾。读取操作以及 fseekfrewind 函数可移动文件位置指示符。

使用 feof 测试 EOF

当使用 textscanfscanffread 一次读取数据的一部分时,请使用 feof 检查是否已到达文件的末尾。

例如,假定所假设的文件 mymeas.dat 具有下列形式,并且没有有关测量集数的信息。将数据读取到具有表示 mtimemdatemeas 的字段的结构体:

12:00:00
01-Jan-1977
4.21  6.55  6.78  6.55
9.15  0.35  7.57  NaN
7.92  8.49  7.43  7.06
9.59  9.33  3.92  0.31
09:10:02
23-Aug-1990
2.76  6.94  4.38  1.86
0.46  3.17  NaN   4.89
0.97  9.50  7.65  4.45
8.23  0.34  7.95  6.46

读取文件:

filename = 'mymeas.dat';
measrows = 4;
meascols = 4;

% open the file
fid = fopen(filename);

% make sure the file is not empty
finfo = dir(filename);
fsize = finfo.bytes;

if fsize > 0 

    % read the file
    block = 1;
    while ~feof(fid)
        mystruct(block).mtime = fscanf(fid, '%s', 1);
        mystruct(block).mdate = fscanf(fid, '%s', 1);

        % fscanf fills the array in column order,
        % so transpose the results
        mystruct(block).meas  = ...
          fscanf(fid, '%f', [measrows, meascols])';

        block = block + 1;
    end

end

% close the file
fclose(fid);

使用 fgetl 和 fgets 测试 EOF

如果在控制循环中使用 fgetlfgetsfeof 并不始终是检测文件末尾的最佳方式。作为备选方法,可以考虑检查 fgetlfgets 返回的值是否为字符向量。

例如,逐行读取数据中描述的 litcount 函数包括下列 while 循环和 fgetl 调用:

y = 0;
tline = fgetl(fid);
while ischar(tline)
   matches = strfind(tline, literal);
   num = length(matches);
   if num > 0
      y = y + num;
      fprintf(1,'%d:%s\n',num,tline);
   end
   tline = fgetl(fid);
end

此方法比检测 ~feof(fid) 更稳健,原因如下:

  • 如果 fgetlfgets 找到数据,则它们返回字符向量。否则,它们返回数值 (-1)。

  • 每次读取操作之后,fgetlfgets 都会检查文件中的下一个字符是否为文件结尾标记。因此,这些函数有时会在返回值 -1 之前设置文件结尾指示符。例如,假设以下包含三行的文本文件。前两行的每一行都以换行符结尾,第三行仅包含文件结尾标记:

    123
    456
    

    fgetl 的三个连续调用生成以下结果:

    t1 = fgetl(fid);    % t1 = '123', feof(fid) = false
    t2 = fgetl(fid);    % t2 = '456', feof(fid) = true
    t3 = fgetl(fid);    % t3 = -1,    feof(fid) = true
    

    此行为不符合相关 C 语言函数的 ANSI 设定。

使用不同的字符编码打开文件

编码方案支持特定字母所需的字符,例如日语或欧洲语言字符。常见的编码方案包括 US-ASCII 或 UTF-8。

如果在打开文件进行读取时没有指定编码方案,fopen 会使用自动字符集检测来确定编码。如果在打开文件进行写入时没有指定编码方案,fopen 默认使用 UTF-8,以便在所有平台和区域设置之间提供互操作性,而不会丢失或损坏数据。

要确定默认编码方案,请打开文件,并再次使用以下语法调用 fopen

[filename, permission, machineformat, encoding] = fopen(fid);

如果在打开文件时指定了编码方案,以下函数将应用该方案:fscanffprintffgetlfgetsfreadfwrite

有关支持的编码方案的完整列表以及用于指定编码的语法,请参阅 fopen 参考页。