【问题标题】:Read a whole text file into a MATLAB variable at once一次将整个文本文件读入 MATLAB 变量
【发布时间】:2011-04-02 00:56:00
【问题描述】:

我想一步将一个(相当大的)日志文件读入 MATLAB 字符串单元格。我用的是平常的:

s={};
fid = fopen('test.txt');
tline = fgetl(fid);
while ischar(tline)
   s=[s;tline];
   tline = fgetl(fid);
end

但这只是很慢。我发现了

fid = fopen('test.txt');
x=fread(fid,'*char');

更快,但我得到了一个 nx1 字符矩阵 x。我可以尝试将x 转换为字符串单元格,但随后我进入了字符编码地狱;行分隔符似乎是 \n\r,或 ASCII 中的 10 和 56(我已经查看了第一行的末尾),但这两个字符通常不会相互跟随,有时甚至会单独出现。

有没有一种简单快速的方法可以一步将 ASCII 文件读入字符串单元格,或将x 转换为字符串单元格?

通过 fgetl 读取:

Code                           Calls        Total Time      % Time
tline = lower(fgetl(fid));     903113       14.907 s        61.2%

通过 fread 阅读:

>> tic;for i=1:length(files), fid = open(files(i).name);x=fread(fid,'*char*1');fclose(fid); end; toc

Elapsed time is 0.208614 seconds.

我已经测试了预分配,但它没有帮助:(

files=dir('.');
tic
for i=1:length(files),   
    if files(i).isdir || isempty(strfind(files(i).name,'.log')), continue; end
    %# preassign s to some large cell array
    sizS = 50000;
    s=cell(sizS,1);

    lineCt = 1;
    fid = fopen(files(i).name);
    tline = fgetl(fid);
    while ischar(tline)
       s{lineCt} = tline;
       lineCt = lineCt + 1;
       %# grow s if necessary
       if lineCt > sizS
           s = [s;cell(sizS,1)];
           sizS = sizS + sizS;
       end
       tline = fgetl(fid);
    end
    %# remove empty entries in s
    s(lineCt:end) = [];
end
toc

经过的时间是 12.741492 秒。

比原来快大约 10 倍:

s = textscan(fid, '%s', 'Delimiter', '\n', 'whitespace', '', 'bufsize', files(i).bytes);

我必须将 'whitespace' 设置为 '' 以保留前导空格(我需要解析),并将 'bufsize' 设置为文件大小(默认 4000 引发缓冲区溢出错误)。

【问题讨论】:

  • 好吧,你获得了 2 秒 :)。无论如何,Matlab 中的低级 fileIO 函数具有惊人的大开销。我在演示 SSD RAID 时了解到这一点,发现速度几乎没有任何提升。

标签: file matlab file-io ascii cell


【解决方案1】:

您的第一个示例运行缓慢的主要原因是 s 在每次迭代中都会增长。这意味着重新创建一个新数组,复制旧行并添加新行,这会增加不必要的开销。

为了加快速度,您可以预先分配s

%# preassign s to some large cell array
s=cell(10000,1);
sizS = 10000;
lineCt = 1;
fid = fopen('test.txt');
tline = fgetl(fid);
while ischar(tline)
   s{lineCt} = tline;
   lineCt = lineCt + 1;
   %# grow s if necessary
   if lineCt > sizS
       s = [s;cell(10000,1)];
       sizS = sizS + 10000;
   end
   tline = fgetl(fid);
end
%# remove empty entries in s
s(lineCt:end) = [];

这是一个小例子,说明预分配可以为您做什么

>> tic,for i=1:100000,c{i}=i;end,toc
Elapsed time is 10.513190 seconds.

>> d = cell(100000,1);
>> tic,for i=1:100000,d{i}=i;end,toc
Elapsed time is 0.046177 seconds.
>> 

编辑

作为fgetl 的替代品,您可以使用TEXTSCAN

fid = fopen('test.txt');
s = textscan(fid,'%s','Delimiter','\n');
s = s{1};

这会将test.txt 的行作为字符串一次性读取到单元格数组s 中。

【讨论】:

  • 我正要给出相同的答案,但有一些我不明白:单元格数组的每个单元格的内容是未定义的。在这种情况下,预分配有帮助吗?
  • 太好了,谢谢。可以肯定的是,我用不同长度的字符串复制了它,你仍然获得了巨大的性能提升。
  • 感谢您的快速回答!我已经对示例进行了编码以显示一般问题,但没有想到示例中可能不预先分配会减慢速度的事实。但是,在我的情况下,我会立即解析这些行,即没有字符串 cell 。分析导致大约 60% 的时间花在“tline = fgetl(fid);”这一行上(其他代码暂时没有优化)。
  • @stephan hattinger:你做什么样的解析?你可以使用 textscan 或 fscanf 立即进行解析吗?
  • 这是相当复杂且上下文敏感的解析。大多数行甚至都不让我感兴趣,每个文件只有大约 200 行长的块。所以我要做的是:找到块入口标记,读取行直到块结束标记,并将字符串单元格传递给递归解析例程(块通常是非常嵌套的对象的缩进打印(也有数组))跨度>
【解决方案2】:

我倾向于为此使用 urlread,例如:

filename = 'test.txt';
urlname = ['file:///' fullfile(pwd,filename)];
try
    str = urlread(urlname);
catch err
    disp(err.message)
end

然后变量 str 包含一大块字符串类型的文本(准备好进行正则表达式操作)。

【讨论】:

    【解决方案3】:

    使用fgetl 函数代替fread。欲了解更多信息,请转至here

    【讨论】:

      【解决方案4】:

      s = regexp(fileread('test.txt'), '(\r\n|\n|\r)', '拆分');

      Matlab 的正则表达式文档中的贝壳示例是直接的。

      【讨论】:

        【解决方案5】:

        下面的方法是基于上面 Jonas 提出的,我非常喜欢。然而,我们得到的是一个元胞数组 s。而不是单个字符串。

        我发现多了一行代码,我们可以得到一个字符串变量,如下所示:

        % original codes, thanks to Jonas
        
        fid = fopen('test.txt');
        s = textscan(fid,'%s','Delimiter','\n');
        s = s{1};
        
        % the additional one line to turn s to a string
        s = cell2mat(reshape(s, 1, []));
        

        我发现为 jsondecode(text) 准备文本很有用。 :)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-07-07
          相关资源
          最近更新 更多