【问题标题】:Read a text file and separate the data into different columns and different tables in MATLAB读取文本文件并将数据分隔到 MATLAB 中的不同列和不同表中
【发布时间】:2016-05-06 03:15:17
【问题描述】:

我有一个巨大的文本文件,需要在 MATLAB 中读取和处理。该文件在某些​​时候包含指示新数据系列已开始的文本。 我在这里搜索过,但找不到任何简单的解决方案。

所以我想做的是读取文件中的数据,将数据放在三个不同列的表中,当它找到文本时,它应该创建一个新表。它应该重复此过程,直到扫描整个文档。

这是文档的样子:

    time    V(A,B)  I(R1)
    Step Information: X=1  (Run: 1/11)
    0.000000000000000e+000  -2.680148e-016  0.000000e+00
    9.843925313007988e-012  -4.753470e-006  2.216314e-011
    1.000052605772457e-011  -4.835427e-006  2.552497e-011
    1.031372754715773e-011  -4.999340e-006  -3.042096e-012
    1.094013052602406e-011  -5.327165e-006  -1.206968e-011
    Step Information: X=1  (Run: 2/11)
    0.000000000000000e+000  -2.680148e-016  0.000000e+000
    9.843925313007988e-012  -4.753470e-006  2.216314e-011
    1.000052605772457e-011  -4.835427e-006  2.552497e-011
    1.031372754715773e-011  -4.999340e-006  -3.042096e-012
    1.094013052602406e-011  -5.327165e-006  -1.206968e-011

【问题讨论】:

  • 最简单的解决方案是对数字数据和文本数据分别调用textscan
  • 每次运行的步数是否恒定?

标签: matlab text file-io text-parsing


【解决方案1】:

一种相当粗略的方法是逐行读取文件并检查该行是否包含三个数字。如果是这样,则将其附加到临时矩阵。当您最终到达不包含三个数字的行时,将此矩阵作为一个元素附加到元胞数组中,清除临时矩阵并继续。

假设文件存储在'file.txt'

%// Open the file
f = fopen('file.txt', 'r');

%// Initialize empty cell array
data = {};

%// Initialize temporary matrix
temp = [];

%// Loop over the file...
while true
    %// Get a line from the file
    line = fgetl(f);

    %// If we reach the end of the file, get out
    if line == -1
        %// Last check before we break
        %// Check if the temporary matrix isn't empty and add
        if ~isempty(temp)
            data = [data; temp];
        end
        break; 
    end

    %// Else, check to see if this line contains three numbers
    numbers = textscan(line, '%f %f %f');

    %// If this line doesn't consist of three numbers...
    if all(cellfun(@isempty, numbers))
        %// If the temporary matrix is empty, skip
        if isempty(temp)
            continue;
        end
        %// Concatenate to cell array
        data = [data; temp];
        %// Reset temporary matrix
        temp = [];
    %// If this does, then create a row vector and concatenate
    else
        temp = [temp; numbers{:}];
    end
end

%// Close the file
fclose(f);

代码非常不言自明,但让我们深入了解它以确保您知道发生了什么。首先使用fopen 打开文件以获得指向该文件的“指针”,然后初始化我们的元胞数组,该数组将包含我们的矩阵以及在读取头信息之间的矩阵时使用的临时矩阵。在我们简单地遍历文件的每一行之后,我们可以使用我们创建的文件指针获取带有fgetl 的行。然后,我们检查是否已到达文件末尾,如果已到达,让我们检查临时矩阵中是否包含任何数字数据。如果是这样,请将其添加到我们的单元格数组中,然后最终退出循环。我们使用fclose 来关闭文件并清理内容。

现在操作的核心是检查之后的内容。我们使用textscan 并搜索三个用空格分隔的数字。这是通过'%f %f %f' 格式说明符完成的。如果您成功使用数字,这应该会给您一个包含三个元素的元胞数组。如果这是正确的,则将此元素元胞数组转换为一行数字并将其连接到临时矩阵中。执行temp = [temp; numbers{:}]; 有助于这种连接。简单地将每个数字拼凑在一起并水平连接它们以创建单行数字。然后我取这一行并将其连接为临时矩阵中的另一行。

如果我们最终到达一个全是文本的行,这将使textscan 找到的元胞数组中的所有三个元素都为空。这就是allcellfun 调用的目的。我们搜索单元格中的每个元素,看看它是否为空。如果每个元素都是空的,则这是一个文本行。如果出现这种情况,只需使用临时矩阵并将其作为新条目添加到您的元胞数组中。然后,您将重置临时矩阵并重新开始逻辑。

但是,我们还必须考虑到可能有多行由文本组成。这就是使用all 在第一个if 块中附加的if 语句的用途。如果我们在前一行文本之前有额外的文本行,则值的临时矩阵应该仍然是空的,因此您应该在尝试连接临时矩阵之前检查它是否为空。如果它是空的,请不要打扰并继续。


运行此代码后,我的数据矩阵得到以下信息:

>> format long g
>> celldisp(data)


data{1} =

                         0             -2.680148e-16                         0
      9.84392531300799e-12              -4.75347e-06              2.216314e-11
      1.00005260577246e-11             -4.835427e-06              2.552497e-11
      1.03137275471577e-11              -4.99934e-06             -3.042096e-12
      1.09401305260241e-11             -5.327165e-06             -1.206968e-11



data{2} =

                         0             -2.680148e-16                         0
      9.84392531300799e-12              -4.75347e-06              2.216314e-11
      1.00005260577246e-11             -4.835427e-06              2.552497e-11
      1.03137275471577e-11              -4.99934e-06             -3.042096e-12
      1.09401305260241e-11             -5.327165e-06             -1.206968e-11

要访问特定的“表格”,请执行data{ii},其中ii 是您要访问的表格,该表格是在您的文本文件中从上到下读取的。

【讨论】:

  • 像这样在每次迭代中连接数据会非常慢。我建议围绕this SO question 中提供的选项来加快速度。
  • @excaza 你可以写一个答案。
  • 我可以,但我没有 MATLAB ;)
  • @excaza 太糟糕了耸耸肩
【解决方案2】:

最通用的方法是使用 textscan 逐行读取。如果您想加快此过程,您可以先进行虚拟读取: IE。您在不存储数据的情况下遍历所有行并确定哪些行是文本行,哪些是数字,并为每行快速记录行数。 然后,您就有足够的关于数据的信息来快速遍历数组。这将加快将数据大量存储在新阵列中所需的时间。 您的第二个循环是实际将数据读入数组的循环。您现在应该知道要跳过哪些行。如果您愿意,还可以在数据单元格中预先分配数组。

fid = fopen('file.txt','r');
data = {};
nlines = [];

% now start the loop
k=0;  % counter for data sets

while ~feof(fid)

    line = fgetl(fid);

    % check if is data or text
    if all(ismember(line,' 0123456789+.')) % is it data
        nlines(k) = nlines(k)+1;
    else                                   %is it text
        k=k+1;
        nlines(k) = 0;
    end
end

frewind(fid);  % go back to start of file

% You could preallocate the data array here if you wished

% now get the data
for aa = 1 : length(nlines)
    if nlines(aa)==0;
        continue
    end
    textscan(fid,'%s\r\n',1); % skip textline
    data{aa} = textscan(fid,'%f%f%f\r\n',nlines(k));
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-11
    • 2013-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多