MATLAB GUI设计入门与实战
上QQ阅读APP看书,第一时间看更新

2.2 Txt文件的读取与写入

Txt文件也是我们常用的文本文件,文本文件的一个好处是可以清楚字符串的格式,并且Txt文件的保存数据和读取数据的快捷性受到业界的支持,本节着重讲解Txt文件的读取与写入操作方法。

2.2.1 Txt文件的打开

MATLAB提供了Txt文本文件的读取功能,并且操作方式有很多,最简单的外部查看Txt文件的操作,如图2-11~图2-12所示。

图2-11 鼠标右键打开

图2-12 MATLAB显示Txt文件

如图2-11所示,单击【打开】命令,则弹出一个类似于MATLAB脚本文件的文件窗口,脚本文件的内容显示和文本文件显示一致,且不会出现乱码的现象,MATLAB软件完全兼容Txt文件。

如图2-11所示,当单击【在MATLAB外部打开】命令时,则采用文本文件查看模式,如图2-13所示。

图2-13 在MATLAB外部打开

如图2-13所示,采用文本文件浏览器打开记事本。

当然,用户也可以用鼠标双击【person.txt】文件,实现打开文本文件,即得到图2-13所示文本浏览器。

2.2.2 Txt文件数据的导入

如图2-14所示data.txt的文件,采用MATLAB读取该文本数据。

图2-14 data.txt数据

如图2-11所示,选择MATLAB自带的【导入数据…】命令,弹出导入数据界面,如图2-15所示。

图2-15 导入数据GUI界面

如图2-15所示,左侧显示为文本文件内数据,数据的维数一目了然,范围为A1:A8,为列矢量,用户只需要单击图2-15箭头指向的按钮即可载入该数据,导入的数据在MATLAB工作区可以查看,如图2-16~图2-17所示。

图2-16 数据导入完成

图2-17 工作区导入读数据

如图2-17所示,导入的数据自动以VarName1命名,以此类推为VarName2、VarName3……等。

如图2-18所示,当文本文件存储为两列数据时,采用外部导入的方法即可,如图2-19所示。

图2-18 两列数据

图2-19 外部导入

如图2-19所示的数据导入,MATLAB导入数据功能,能够自动识别所输入数据。

特别的,当文本文件含有字符变量时,导入功能仍然可以轻松地完成,含字符的文本文件如图2-20所示,选择【导入数据…】命令,得到如图2-21所示结果。

图2-20 带字符的数据

图2-21 导入数据

如图2-21所示,选择【导入数据…】功能,MATLAB能够自动识别数据,即排除字符干扰,使得用户能够轻松地对有用数据进行高效处理。

然而MATLAB只会识别含字符文档的前几行字符,一旦出现数字即认为后面的所有的行的矢量均为数值类型,如图2-22和图2-23所示。

图2-22 前后带字符的数据

图2-23 数据导入识别结果

选择MATLAB【导入数据…】功能可实现数据的导入,另外MATLAB也提供了相应的可供用户加载数据的函数,例如如图2-23所示的数据导入过程,MATLAB能够自动加载数据,同时也自动生成代码,如图2-24所示。

图2-24 生成脚本和生成函数

(1)如图2-24所示,单击【生成脚本】命令,得到相应的脚本文件,如图2-25所示。

图2-25 脚本文件

保存该文件为Untitled2.m,其代码如下:

    %% 导入文本文件中的数据
    % 用于从以下文本文件导入数据的脚本
    %
    %    F:\MATLAB Edit 2013a\MATLAB Edit 2012B\ysw\book\GUI设计手册\第2章          \data.txt
    %
    % 要将代码扩展到其他选定数据或其他文本文件,请生成函数来代替脚本
    
    %% 初始化变量
    filename = 'F:\MATLAB Edit 2013a\MATLAB Edit 2012B\ysw\book\GUI设计手册\第2章\data.txt';
    delimiter = ' ';                      % 定界符
    startRow = 2;                          % 起始行
    
    %% 将数据列作为字符串读取
    formatSpec = '%s%s%[^\n\r]';          % 将数据列作为字符串读取
    
    %% 打开文本文件
    fileID = fopen(filename,'r');          % 打开文件
    
    %% 根据格式字符串读取数据列
    % 该调用基于生成此代码所用的文件的结构。如果其他文件出现错误,请尝试通过导入工具重新生成代码
    dataArray = textscan(fileID, formatSpec, 'Delimiter', delimiter, 'MultipleDelimsAsOne', true, 'HeaderLines' ,startRow-1, 'ReturnOnError', false);   % 获取数据矩阵
    
    %% 关闭文本文件
    fclose(fileID);
    
    %% 将包含数值字符串的列内容转换为数值
    % 将非数值字符串替换为 NaN
    raw = repmat({''},length(dataArray{1}),length(dataArray)-1); 
    for col=1:length(dataArray)-1
        raw(1:length(dataArray{col}),col) = dataArray{col};                                                      % 结构体转化为数值矩阵
    end
    numericData = NaN(size(dataArray{1},1),size(dataArray,2));
    
    for col=[1,2]
        % 将输入元胞数组中的字符串转换为数值。已将非数值字符串替换为 NaN
        rawData = dataArray{col};
        for row=1:size(rawData, 1);
            % 创建正则表达式以检测并删除非数值前缀和后缀
            regexstr = '(?<prefix>.*?)(?<numbers>([-]*(\d+[\,]*)+[\.]{0,1}\d*
    [eEdD]{0,1}[-+]*\d*[i]{0,1})|([-]*(\d+[\,]*)*[\.]{1,1}\d+[eEdD]{0,1}[-+]*\d*[i]{0,1}))(?<suffix>.*)';
            try
                result = regexp(rawData{row}, regexstr, 'names');
                numbers = result.numbers;
                
                % 在非千位位置中检测到逗号
                invalidThousandsSeparator = false;
                if any(numbers==',');
                    thousandsRegExp = '^\d+?(\,\d{3})*\.{0,1}\d*$';
                    if isempty(regexp(thousandsRegExp, ',', 'once'));
                        numbers = NaN;
                        invalidThousandsSeparator = true;
                    end
                end
                % 将数值字符串转换为数值
                if ~invalidThousandsSeparator;
                    numbers = textscan(strrep(numbers, ',', ''), '%f');
                    numericData(row, col) = numbers{1};
                    raw{row, col} = numbers{1};
                end
            catch me
            end
        end
    end
    
    
    %% 将非数值元胞替换为 NaN
    R = cellfun(@(x) ~isnumeric(x) && ~islogical(x),raw); % 查找非数值元胞
    raw(R) = {NaN}; % 替换非数值元胞
    
    %% 将导入的数组分配给列变量名称
    ysw = cell2mat(raw(:, 1));         % cell转化为mat格式文件
    person = cell2mat(raw(:, 2));      % cell转化为mat格式文件
    
    %% 清除临时变量
    clearvars filename delimiter startRow formatSpec fileID dataArray ans raw col numericData rawData row regexstr result numbers invalidThousandsSeparator thousandsRegExp me R;

用户可直接运行Untitled2.m,则MATLAB工作区将自动得到相应的加载数据。

(2)如图2-24所示,单击【生成函数】命令,得到相应的函数文件,如图2-26所示。

图2-26 自动生成加载数据函数文件

如图2-26所示,MATLAB自动生成一个importfile1.m函数文件,用户可以输入数据,即可实现数据的加载,具体的importfile1.m函数文件如下:

    function [ysw1,person1] = importfile1(filename, startRow, endRow)
    %IMPORTFILE1 将文本文件中的数值数据作为列矢量导入
    %   [YSW1,PERSON1] = IMPORTFILE1(FILENAME) 读取文本文件 FILENAME 中默认选定范围的数据
    %
    %   [YSW1,PERSON1] = IMPORTFILE1(FILENAME, STARTROW, ENDROW) 读取文本文件
    %   FILENAME的STARTROW起始行到ENDROW终止行中的数据
    %
    % Example:
    %   [ysw1,person1] = importfile1('data.txt',2, 10);  % 导入数据调用格式
    
    %% 初始化变量
    delimiter = ' ';  % 定界符,终止符
    if nargin<=2
        startRow = 2;  % 起始行
        endRow = inf;  % 结束行
    end
    
    %% 将数据列作为字符串读取
    % 有关详细信息,请参阅 TEXTSCAN 文档
    formatSpec = '%s%s%[^\n\r]';
    
    %% 打开文本文件
    fileID = fopen(filename,'r');
    
    %% 根据格式字符串读取数据列
    % 该调用基于生成此代码所用的文件的结构。如果其他文件出现错误,请尝试通过导入工具重新生成代码
    dataArray = textscan(fileID, formatSpec, endRow(1)-startRow(1)+1, 'Delimiter', delimiter, 'MultipleDelimsAsOne', true, 'HeaderLines', startRow(1)-1, 'ReturnOnError', false);   % 获取数据矩阵
    for block=2:length(startRow)
        frewind(fileID);
        dataArrayBlock = textscan(fileID, formatSpec, endRow(block)-startRow
    (block)+1, 'Delimiter', delimiter, 'MultipleDelimsAsOne', true, 'HeaderLines', startRow(block)-1, 'ReturnOnError', false);
        for col=1:length(dataArray)
            dataArray{col} = [dataArray{col};dataArrayBlock{col}];  % 赋值
        end
    end
    
    %% 关闭文本文件
    fclose(fileID);
    
    %% 将包含数值字符串的列内容转换为数值
    % 将非数值字符串替换为 NaN
    raw = repmat({''},length(dataArray{1}),length(dataArray)-1);
    for col=1:length(dataArray)-1
        raw(1:length(dataArray{col}),col) = dataArray{col};
    end
    numericData = NaN(size(dataArray{1},1),size(dataArray,2));
    
    for col=[1,2]
        % 将输入元胞数组中的字符串转换为数值。已将非数值字符串替换为NaN
        rawData = dataArray{col};
        for row=1:size(rawData, 1);
            % 创建正则表达式以检测并删除非数值前缀和后缀
            regexstr = '(?<prefix>.*?)(?<numbers>([-]*(\d+[\,]*)+[\.]{0,1}\d*[eEdD]{0,1}[-+]*\d*[i]{0,1})|([-]*(\d+[\,]*)*[\.]{1,1}\d+[eEdD]{0,1}[-+]*\d*[i]{0,1}))(?<suffix>.*)';
            try
                result = regexp(rawData{row}, regexstr, 'names');
                numbers = result.numbers;
                
                % 在非千位位置中检测到逗号
                invalidThousandsSeparator = false;
                if any(numbers==',');
                    thousandsRegExp = '^\d+?(\,\d{3})*\.{0,1}\d*$';
                    if isempty(regexp(thousandsRegExp, ',', 'once'));
                        numbers = NaN;
                        invalidThousandsSeparator = true;
                    end
                end
                % 将数值字符串转换为数值
                if ~invalidThousandsSeparator;
                    numbers = textscan(strrep(numbers, ',', ''), '%f');
                    numericData(row, col) = numbers{1};
                    raw{row, col} = numbers{1};
                end
            catch me
            end
        end
    end
    
    %% 将非数值元胞替换为NaN
    R = cellfun(@(x) ~isnumeric(x) && ~islogical(x),raw); % 查找非数值元胞
    raw(R) = {NaN}; % 替换非数值元胞
    
    %% 将导入的数组分配给列变量名称
    ysw1 = cell2mat(raw(:, 1));
    person1 = cell2mat(raw(:, 2));

在MATLAB命令行窗口输入如下代码:

    [ysw1,person1] = importfile1('data.txt',2, 10)   % 调用函数

运行程序输出结果如下:

    ysw1 =
          201517
            5337
             182
            8037
            8241
             117
             406
            3087
             NaN
    
    person1 =
          201517
          201517
          201517
          201517
          201517
          201517
          201517
          201517
             NaN

因此,用户借助该函数文件,也可以实现其他文本文件的加载。而【生成脚本】功能只限于加载某一个指定的文本文件,【生成函数】功能可实现同类型文件的数据导入。MATLAB这个自动生成代码的功能给用户提供了极大的便利。

MATLAB也提供了较简便的文本数据加载函数,具体如下:

    >> ysw = load('data1.txt')   % 加载数据
    
    ysw =
    
          201517        201517
            5337        201517
             182        201517

如果直接使用load('data1.txt'),MATLAB自动以ans.mat保存数据到工作区下,可供MATLAB GUI设计使用,也可供MATLAB主函数文件参数传递使用。

load('data1.txt')函数使用有其局限性,如果文本文件含有字母等变量,直接采用load('data1.txt')是不能加载的,load('data1.txt')只对单纯的数据加载有效,例如data.txt的内容如下:

    ysw person
    201517 201517
    5337 201517
    182 201517
    8037 201517
    8241 201517
    117  201517
    406 201517
    3087 201517
    ysw person

采用load('data.txt')进行调用,系统将提示如下信息:

    >> ysw = load('data.txt')
    错误使用 load
    ASCII 文件 data.txt 的行号 1 中的文本未知
    "ysw"。

因此,用户应该根据具体的问题具体对待,当含有字符类型时,可采用MATLAB importfile函数进行数据分析。

(3)MATLAB提供了向文本文件写入和读入的功能,方便用户存储调用数据,利用MATLAB向ysw.txt文件中添加数据,程序如下:

    % Designed by Yu Shengwei From SWJTU University
    % 2015年1月3日
    clc,clear,close all         % 清理命令区、清理工作区、关闭显示图形
    warning off                 % 消除警告
    feature jit off            % 加速代码运行
    format short                % 数据类型
    tic                         % 运算计时
    A = [1,1,7,4,0,6,3,0,8,7,0;
        1,8,2,8,0,3,7,8,2,4,1;
        1,3,6,7,9,6,1,0,0,4,1;
        1,8,3,8,2,4,5,8,0,7,4];
    % 存储数据A
    save ysw.txt A -ascii;

运行程序输出结果如下:

       1.0000000e+00   1.0000000e+00   7.0000000e+00   4.0000000e+00   0.0000000e+00   6.0000000e+00   3.0000000e+00   0.0000000e+00   8.0000000e+00   7.0000000e+00   0.0000000e+00
       1.0000000e+00   8.0000000e+00   2.0000000e+00   8.0000000e+00   0.0000000e+00   3.0000000e+00   7.0000000e+00   8.0000000e+00   2.0000000e+00   4.0000000e+00   1.0000000e+00
       1.0000000e+00   3.0000000e+00   6.0000000e+00   7.0000000e+00   9.0000000e+00   6.0000000e+00   1.0000000e+00   0.0000000e+00   0.0000000e+00   4.0000000e+00   1.0000000e+00
       1.0000000e+00   8.0000000e+00   3.0000000e+00   8.0000000e+00   2.0000000e+00   4.0000000e+00   5.0000000e+00   8.0000000e+00   0.0000000e+00   7.0000000e+00   4.0000000e+00

由结果可知,MATLAB向文本文件写入数据,默认为longE类型,生成的数据和写入的数据一致,用户可以将txt数据加载到MATLAB进行分析,因此在MATLAB中文本文件数据写入较简便。

当MATLAB再次向同一文本文件写入数据时,MATLAB将自动清空该文本文件中的所有数据,然后再写入该数据,例如再次写入数据如下:

    A = [1,1,7,4,0,6,3,0,8,7;
        1,8,2,8,0,3,7,8,2,4;
        1,3,6,7,9,6,1,0,0,4;
        1,8,3,8,2,4,5,8,0,7];

运行程序输出结果如下。

       1.0000000e+00   1.0000000e+00   7.0000000e+00   4.0000000e+00   0.0000000e+00   6.0000000e+00   3.0000000e+00   0.0000000e+00   8.0000000e+00   7.0000000e+00
       1.0000000e+00   8.0000000e+00   2.0000000e+00   8.0000000e+00   0.0000000e+00   3.0000000e+00   7.0000000e+00   8.0000000e+00   2.0000000e+00   4.0000000e+00
       1.0000000e+00   3.0000000e+00   6.0000000e+00   7.0000000e+00   9.0000000e+00   6.0000000e+00   1.0000000e+00   0.0000000e+00   0.0000000e+00   4.0000000e+00
       1.0000000e+00   8.0000000e+00   3.0000000e+00   8.0000000e+00   2.0000000e+00   4.0000000e+00   5.0000000e+00   8.0000000e+00   0.0000000e+00   7.0000000e+00

为了直观地说明该问题,写入1,具体程序如下:

    A=[1];
    %存储数据A
    save ysw.txt A -ascii;

运行程序输出结果如下:

       1.0000000e+00

MATLAB还提供了一个文本文件数据追加功能,即向已经生成的文本文件追加数据,而不是对原先数据进行覆盖处理,那么怎么进行数据追加呢?具体操作如下:

    clc,clear,close all          % 清理命令区、清理工作区、关闭显示图形
    warning off                     % 消除警告
    format short                    % 数据类型
    tic                             % 运算计时
    A = [1,1,7,4,0,6,3,0,8,7;
        1,8,2,8,0,3,7,8,2,4;
        1,3,6,7,9,6,1,0,0,4;
        1,8,3,8,2,4,5,8,0,7];     % 矩阵A
    B=[1];
    % 存储数据A
    save ysw.txt A -ascii;
    % 追加存储数据N
    save ysw.txt B -append -ascii;
    toc  % 计时结束

追加数据后,ysw.txt文本文件数据如下:

       1.0000000e+00   1.0000000e+00   7.0000000e+00   4.0000000e+00   0.0000000e+00   6.0000000e+00   3.0000000e+00   0.0000000e+00   8.0000000e+00   7.0000000e+00
       1.0000000e+00   8.0000000e+00   2.0000000e+00   8.0000000e+00   0.0000000e+00   3.0000000e+00   7.0000000e+00   8.0000000e+00   2.0000000e+00   4.0000000e+00
       1.0000000e+00   3.0000000e+00   6.0000000e+00   7.0000000e+00   9.0000000e+00   6.0000000e+00   1.0000000e+00   0.0000000e+00   0.0000000e+00   4.0000000e+00
       1.0000000e+00   8.0000000e+00   3.0000000e+00   8.0000000e+00   2.0000000e+00   4.0000000e+00   5.0000000e+00   8.0000000e+00   0.0000000e+00   7.0000000e+00
       1.0000000e+00

由此,我们将很容易向文本文件中添加数据或删减数据。