【问题标题】:Split large strings by delimiters通过分隔符拆分大字符串
【发布时间】:2014-03-02 11:45:55
【问题描述】:

我正在尝试处理system('./foo') 命令的输出。如果我直接将输出重定向到带有system('./foo > output') 的文件并通过dlmread 将文件读入MATLAB,它工作正常,但我试图避免在硬盘上每次写入一个巨大的ASCII 文件(大约1e7 行)是时候做这个了。

所以我想直接处理输出,将其读入一个巨大的字符串并拆分字符串。它适用于小文件:

[a,b] = system('./foo')
b=strsplit(b);
cellfun(@str2num, bb);
b=cellfun(@str2num, b(1:end),'UniformOutput',0);
b=cell2mat(b);

不幸的是,这已经在strsplit 操作的步骤中消耗了太多内存,以至于 MATLAB 被 OOM 杀手杀死。 我找到了替代方案:

b=textscan(b,'%s','delimiter',' ','multipleDelimsAsOne',1);

但它也消耗了太多的内存。

有人可以帮助我更好地了解如何拆分该数字字符串并将其读入矩阵,或者通常如何避免将命令的输出写入硬盘上的文件?

编辑:(我在这里写,因为在 cmets 中没有足够的空间......) @MZimmerman6 我现在尝试了 dlmread 的一个版本,有和没有预分配以及你的建议以及我理解的: 事实上,循环比 dlmread 慢得多。

clear all
close all
tic 
ttags1=dlmread('tmp.txt',' ',1,3);

toc

clear all

tic
[~,result]=system('perl -e ''while(<>){};print$.,"\n"'' tmp.txt');
numLines1=str2double(result);
ttags=zeros(numLines1,1);
ttags=dlmread('tmp.txt',' ',1,3);

toc

clear all

tic 
fid = fopen('tmp.txt');
count = 1;
[~,result]=system('perl -e ''while(<>){};print$.,"\n"'' tmp.txt');
numLines1=str2double(result);
temp = cell(numLines1,1);
for i = 1:numLines1
    tline = fgetl(fid);
    if ischar(tline)
        vals = textscan(tline,'%f','delimiter',',');
        temp{i} = transpose(vals{1});
    end
end
fclose(fid);
temp  = cell2mat(temp);

 toc

结果是:

Elapsed time is 19.762470 seconds.
Elapsed time is 21.546079 seconds.
Elapsed time is 796.755343 seconds.

谢谢你和最好的问候

我做错了吗?

【问题讨论】:

    标签: string matlab large-data


    【解决方案1】:

    您不应该尝试将整个文件读入内存,因为这可能会占用大量内存。我建议逐行读取文件,并单独处理每个文件,然后将结果存储到一个单元格数组中。然后,您可以在解析完成后将其转换为普通矩阵。

    我可以做的第一件事是创建一个小的 Perl 脚本来计算您正在阅读的文件中的行数,这样您就可以为数据预先分配内存。将此文件称为countlines.pl。从here收集的信息

    Perl - Countlines.pl

     while (<>) {};
     print $.,"\n";
    

    这个文件只有两行,但会快速计算文件中的总行数。

    然后您可以使用此文件的结果进行预分配,然后逐行解析。我在测试中使用了一个简单的逗号分隔文件,因此您可以调整 textscan 以根据需要处理事情。

    MATLAB 脚本

    % get number of lines in data file
    numLines = str2double(perl('countlines.pl','text.txt'));
    fid = fopen('text.txt');
    count = 1;
    temp = cell(numLines,1);
    for i = 1:numLines
        tline = fgetl(fid);
        if ischar(tline)
            vals = textscan(tline,'%f','delimiter',',');
            temp{i} = transpose(vals{1});
        end
    end
    fclose(fid);
    temp  = cell2mat(temp);
    

    这应该根据您的文件大小运行相对较快,并按照您的意愿行事。当然,您可以编辑如何在循环内完成解析,但这应该是一个很好的起点。

    注意以后,如果不是完全必要,不要尝试将大量内容读入内存

    【讨论】:

    • +1。其他快速计算行数的方法here.
    • 不能使用类似下面的东西:[~,result]=system('perl -e ''while(&lt;&gt;){};print$.,"\n"'' text.txt');numLines=str2double(result);?还是使用文件会使其更快,或者这只是不是跨平台的?
    • 好的,谢谢您的回答!我确实会检查我是否可以避免一次加载整个文件。我只是想知道为什么您使用单元阵列来读取数据?如果我知道我想读取数字,那是否比用 zeros(numLines,1) 分配空间更好?再次感谢 horchler!
    • 我使用元胞数组的唯一原因是因为它在预分配空间时更加动态,而且如果您的文件包含字符串和数字,则元胞数组可以包含多种数据类型,而矩阵不能。 @horchler 至于单独的文件是否会使其更快,我不知道,但我喜欢拥有该文件,以便我可以通过简单地将 perl 文件放在 MATLAB 根目录中来在多个地方重用代码。只是个人喜好
    猜你喜欢
    • 1970-01-01
    • 2013-10-30
    • 2013-03-03
    • 2018-05-05
    • 2011-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-21
    相关资源
    最近更新 更多