Matlab:加速读取ascii文件

时间:2013-05-19 08:58:49

标签: performance matlab ascii

我编写了这段代码,它的工作正常,但对我的目的来说太慢了:

%%% load nodal data %%% 
path = sprintf('%sfile.dat',directory);
fid = fopen(path);

num_nodes = textscan(fid,'%s %s %s %s %d',1,'delimiter', ' ');
num_nodes = num_nodes{5};
header = textscan(fid,'%s',7,'delimiter', '\t');

k = 0;
while ~feof(fid)

    line        = fgetl(fid);
    [head,rem]  = strtok(line,[' ',char(9)]); 

    if head == '#'
        k = k+1;
        j = 1;
        time_steps(k)  = sscanf(rem, [' Output at t = %d']);        
    end

    if ~isempty(head)
        if head ~= '#'
            data(j,:,k)  = str2num([head rem]); 
            j = j+1;
        end
    end

end
fclose(fid);

nodal_data = struct('header',header,'num_nodes',num_nodes,'time_steps',time_steps,'data',data);

我正在阅读Matlab的ascii看起来像这样:

# Number of Nodes: 120453
#X                  Y                   Z                   depth               vel_x               vel_y               wse             
# Output at t = 0
       76456.003              184726             3815.75                   0                   0                   0             3815.75
       76636.003              184726             3728.25                   0                   0                   0             3728.25
       76816.003              184726                3627                   0                   0                   0                3627
       76996.003              184726             3527.75                   0                   0                   0             3527.75
       77176.003              184726              3371.5                   0                   0                   0              3371.5
# Output at t = 36000.788
       76456.003              184726             3815.75                   0                   0                   0             3815.75
       76636.003              184726             3728.25                   0                   0                   0             3728.25
       76816.003              184726                3627                   0                   0                   0                3627
       76996.003              184726             3527.75                   0                   0                   0             3527.75
       77176.003              184726              3371.5                   0                   0                   0              3371.5

虽然我编写的代码适用于非常小的文件,但是对于更大的ascii文件,它会让我感到震惊。我已经不得不中止加载~25mb ascii(大约240k行),这只是一个测试文件。该文件的更新版本将是~500mb。有没有办法加快加载文件的过程我对3 if语句不满意,但我不知道如何用开关打开数字来分隔'#',特别是因为我无法通过类来区分'head',即我试图检查ischar或isnumeric,但是当变量'head'被读作字符串时,它始终是ischar的情况,永远不会{{1} } = isnumeric。我也不太乐意使用一个标记器来使用if-cases,然后在这里放置一行:true,因为这可能会消耗很多时间。但是,我不知道怎么做。 因此,如果您对如何调整我的代码有任何有用的建议,我将非常感谢他们!

祝你周日愉快,并提前感谢你!

2 个答案:

答案 0 :(得分:2)

下面的代码读取大约70000次步长,每步5个节点,大约7秒钟。它完成了代码所做的大部分工作,并且应该很容易添加代码的额外功能。还有其他方法可以更快地完成这项工作,但希望这应该足够了。

filename = 'd:\temp\input.txt';

filetext = fileread(filename);
headerLines = 2;
valuesPerLine = 7;
expr = '[^\n]*[^\n]*';
lines = regexp(filetext, expr, 'match');
isTimeStep = cellfun(@(x) strncmp(x,'#',1), lines );
numTimeSteps = sum(isTimeStep)-headerLines;
nodesPerStep = ((length(lines)-headerLines) / numTimeSteps ) - 1;
data = zeros(nodesPerStep, valuesPerLine, numTimeSteps);

for timeStep = 1:numTimeSteps
    lineIndex = headerLines + (timeStep-1) * (nodesPerStep + 1) + 2;
    for node = 1:nodesPerStep
        data(node, :, timeStep ) = sscanf(lines{lineIndex},'%f');
        lineIndex = lineIndex + 1;
    end    
end

刚试了200万行文件(340000个步骤,每步5个节点),大约需要36秒才能运行。

如果您想要一个没有编码循环的解决方案,您可以从

中的代码替换
data = zeros(....

values = cellfun(@(x) sscanf(x,'%f'),lines(~isTimeStep),'uniformoutput',false);
data = reshape(cell2mat(values), nodesPerStep, valuesPerLine, numTimeSteps);

但运行时间要长约50%。

答案 1 :(得分:1)

更改任何内容之前要做的第一件事是 PRE-ALLOCATE 所有输出数组:
您的代码输出time_stepsdata,所有这些都在循环中生长。这会让你失去性能。

假设每个时间步之间总共有五行

在循环

之前添加以下行
data = reshape( NaN( num_nodes, 7 ), [], 7, 5 ); % assuming 7 columns and 5 lines for each time step
time_steps = NaN( num_nodes / 5 );
循环之后

只丢弃剩余的NaN

data( isnan(data) ) = [];
time_step( isnan(time_step) ) = [];