【问题标题】:Reading and processing a large text file in Matlab在 Matlab 中读取和处理大型文本文件
【发布时间】:2011-05-10 16:21:49
【问题描述】:

我正在尝试将一个大型文本文件(几百万行)读入 Matlab。最初我使用的是 importdata(file_name),这似乎是一个简洁的解决方案。但是我需要使用 Matlab 7(是的,我知道它的旧版本)而且似乎不支持 importdata。因此,我尝试了以下方法:

while ~feof(fid)    
    fline = fgetl(fid);
    fdata{1,lno} =  fline ;
    lno = lno + 1;
end

但这真的很慢。我猜它是因为它在每次迭代时调整数组的大小。有没有更好的方法来做到这一点。请记住,输入数据的前 20 行是字符串类型数据,其余数据是 3 到 6 列的十六进制值。

【问题讨论】:

    标签: file matlab io


    【解决方案1】:

    您将不得不进行一些重塑,但您可以选择使用 fread。 但正如前面提到的,这实际上将您锁定在一个矩形导入中。所以另一种选择是使用 textscan。正如我在另一条笔记中提到的,我不能 100% 确定它何时实施,我只知道你没有“importdata()”

    fid = fopen('textfile.txt')
    Out  = textscan(fid,'%s','delimiter',sprintf('\n'));
    fclose(fid)
    

    使用 textscan,您将能够为每一行获取一个字符元胞数组,然后您可以根据需要进行操作。正如我在我的 cmets 中所说,线条是否相同长度不再重要。现在您可以更快地解析元胞数组。但正如 gnovice 所提到的,他也确实有一个非常优雅的解决方案,您可能不得不关注内存要求。

    如果可以避免的话,您永远不想在 matlab 中使用它,那就是循环结构。它们在 C/C++ 等中速度很快,但在 matlab 中,它们是到达目的地的最慢方法。

    编辑:刚刚查了一下,看起来 textscan WAS 是在版本 7 (R14) 中实现的,所以如果你有的话,你应该很好地使用它。

    【讨论】:

    • 哦,现在你只是在这里测试我的技能 :) 我已经编辑了我的帖子,以反映一种你可以在没有相同长度的线条的情况下逃脱的方式。
    • TEXTSCAN 的好消息。我不确定这在第 7 版中是否可用。我的基于 FSCANF 的解决方案基本上做同样的事情,但输入更多:)。
    • 是的,这就是我支持你的原因。它是一个非常好的解决方案,它利用分配的解决方法来解决可能缺少的功能。当 Matlab 没有它的函数时,for 或 while 循环几乎不是一个好的起点!我宁愿编写自己的 MEX,也不愿每次都经历数十亿次 for 循环迭代 :)
    【解决方案2】:

    我看到两个选项:

    1. 您可以每次增长 1,例如加倍 仅在必要时将数组的大小加倍。这大大减少了所需的重新分配次数。
    2. 进行两次进近。第一遍只计算行数,而不存储它们。第二遍实际上填充了数组(已预先分配到正确的大小)。

    【讨论】:

    • 看起来很合理——我该怎么做。因为与普通数组相比,使用 {} 单元格让我有点困惑。
    • @trican:每次lno 达到 2 的幂(例如),然后将 fdata 与 1xlno 空矩阵元胞数组连接起来,即fdata = [fdata cell(1,lno)]
    • 您还可以通过分配给超出末尾的元素来扩展它:fdata(end*2) = fdata(1);。适用于大多数数据类型。
    【解决方案3】:

    一种解决方案是使用FSCANF 将文件的全部内容作为字符串读取,然后使用MAT2CELL 在出现换行符的位置将字符串拆分为单独的单元格,使用STRTRIM,然后根据需要处理每个单元格中的字符串数据。例如,使用这个示例文本文件'junk.txt'

    hi
    hello
    1 2 3
    FF 00 FF
    12 A6 22 20 20 20
    FF FF FF
    

    以下代码会将每一行放入元胞数组cellData的一个元胞中:

    >> fid = fopen('junk.txt','r');
    >> strData = fscanf(fid,'%c');
    >> fclose(fid);
    >> nCharPerLine = diff([0 find(strData == char(10)) numel(strData)]);
    >> cellData = strtrim(mat2cell(strData,1,nCharPerLine))
    
    cellData = 
    
        'hi'    'hello'    '1 2 3'    'FF 00 FF'    '12 A6 22 20 20 20'    'FF FF FF'
    

    现在,如果您想将所有十六进制数据(我的示例数据文件中的第 3 到 6 行)从字符串转换为数字向量,您可以使用 CELLFUNSSCANF,如下所示:

    >> cellData(3:end) = cellfun(@(s) {sscanf(s,'%x',[1 inf])},cellData(3:end));
    >> cellData{3:end}    %# Display contents
    
    ans =
    
         1     2     3
    
    ans =
    
       255     0   255
    
    ans =
    
        18   166    34    32    32    32
    
    ans =
    
       255   255   255
    

    注意:由于您正在处理如此大的数组,因此您必须注意变量中的amount of memory being used。上述解决方案是矢量化的,但可能会占用大量内存。创建cellData 时,您可能必须覆盖或clearstrData 这样的大变量。或者,您可以遍历 nCharPerLine 中的元素,并将较大字符串 strData 的每个段单独处理为您需要的向量,现在您可以预分配,因为您知道有多少行数据你有(即nDataLines = numel(nCharPerLine)-nHeaderLines;)。

    【讨论】:

      猜你喜欢
      • 2012-02-07
      • 1970-01-01
      • 1970-01-01
      • 2015-07-07
      • 2015-10-10
      • 2021-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多