分享

(1)MATLAB读写文件

 yidiantou 2015-07-02
如何读取特定规则的文本中的数据呢?
假定名为 test.txt 的文件中以下为文件内容 

你好,我的数据 
欢迎来到 
百思论坛 
www.baisi.net 
1 11 111 1111 
2 22 222 2222 
3 33 333 3333 
4 44 444 4444 
5 55 555 5555 

这样的文件怎么读入数据呢? 
方法有多种,现举两个比较简单实用的。 

方法一: 
A=importdata(file,space,line)
其中,file是所读取的文件名,space是特定的分隔符,line是一个数字,指文本中字符串文字的行数,如上文的数据中line=4。
此代码的含义是,line行是title,line+1行到end是数据,按特定分隔符space读取数据。此方法可以读取文本在上和数据在下的文件。
A是一个sturct,其中A.data就是所需的数据了。
exapmle:
A=importdata('test.txt',' ',4);%运行即可取得数据A.data
 
此方法也可以按以下操作获取:
在文件菜单中选择 file/import data,按照提示进行操作至结束。 
 command 窗口中输入 
>> whos 
Name           Size                   Bytes  Class 

data           5x4                      160  double array 
textdata       4x1                      300  cell array 

Grand total is 54 elements using 460 bytes 

>> data 

data = 

                  11         111        1111 
                  22         222        2222 
                  33         333        3333 
                  44         444        4444 
                  55         555        5555 

>> textdata 

textdata = 

   '你好
   '欢迎来到
   '百思论坛
   'www.baisi.net'

方法二: 

[a1,a2,a3,a4]=textread('test1.txt','%s%s%s%s','headerlines',4) 
说明:%s可以是其他形式,跟读入的数据类型有关,比如这里也可以用%d,%f等。 
这里%s的个数和[a1,a2,a3,a4]对应。 
>> [a1,a2,a3,a4]=textread('test1.txt','%s%s%s%s','headerlines',4) 

a1 = 

   '1' 
   '2' 
   '3' 
   '4' 
   '5' 


a2 = 

   '11' 
   '22' 
   '33' 
   '44' 
   '55' 


a3 = 

   '111' 
   '222' 
   '333' 
   '444' 
   '555' 


a4 = 

   '1111' 
   '2222' 
   '3333' 
   '4444' 
   '5555' 
因以字符串的形式读入,所以有'' 

文件内容形式二(假定文件名为test2.txt): 

你好 
1 11 111 1111 
欢迎来到 
2 22 222 2222 
百思论坛 
3 33 333 3333 
www.baisi.net 
4 44 444 4444 
5 55 555 5555 

说明:这种内容格式的文件用上面的方法是不行的。 

方法三:
以下是由chinamaker编写的一种方法,但是需要重新建一个文本。
fidin=fopen('test2.txt');                              打开test2.txt文件              
fidout=fopen('mkmatlab.txt','w');                       创建MKMATLAB.txt文件 
while ~feof(fidin)                                      判断是否为文件末尾                
   tline=fgetl(fidin);                                 从文件读行    
   if double(tline(1))>=48&&double(tline(1))<=57      判断首字符是否是数值 
      fprintf(fidout,'%s\n\n',tline);                  如果是数字行,把此行数据写入文件MKMATLAB.txt 
      continue                                         如果是非数字继续下一次循环 
   end 
end 
fclose(fidout); 
MK=importdata('MKMATLAB.txt');      将生成的MKMATLAB.txt文件导入工作空间,变量名为MK,实际上它不显示出来 
>> MK 

MK = 

                  11         111        1111 
                  22         222        2222 
                  33         333        3333 
                  44         444        4444 
                  55         555        5555

另外有些用户希望获取文件的长度(字符个数),哦这个其实很简单!

  1. fid=fopen('data.csv');
  2. fseek(fid,0,'eof')
  3. filelength = ftell(fid);
  4. fclose(fid);

复制代码

现在问题来了,如何获取文本文件的行数呢??下面我们提供几种解决方案吧!本文代码中测试的data.csv数据文件总共有10万行,大概77M。

方法一

最容易想到的就是使用MATLAB的fgetl或fgets函数,对文本按行读取,然后对行数进行累加。

  1. tic
  2. fid=fopen('data.csv'); % 打开文件
  3. row=0;
  4. while ~feof(fid) % 是否读取到文件结尾
  5.     [~]=fgets(fid); % 或者fgetl
  6.     row=row+1; % 行数累加
  7. end
  8. fclose(fid); % 及时关闭文件是个好习惯
  9. row
  10. toc

复制代码

运行结果如下,fgets大概耗时大概10s,如果fgetl速度会慢一些,大概需要13s,可能是因为fgel需要将尾部的回车去掉而增加了操作吧

row =

      100000

Elapsed time is 10.556020 seconds.

方法二

其实MATLAB处理复杂文本文件,灵活性最好和效率最高的是textscan函数。灵活性就不用说了,textscan提供很多定制功能,比如跳过的标题行数、设置空白字符等经过测试;至于效率,经过测试,textscan在处理某些数据下是效率fscanf的10倍以上,另外仔细看看dlmread函数,其实也是调用了textscan函数。

接着有人要问,textscan是用来读取数据的,怎么用来统计行数?其实我们只是占了一个便宜而已!因为textscan提供了一个忽略特定字符串的功能。

  1. tic
  2. fid=fopen('data.csv');
  3. % '%*[^\n]' 这个设置估计有人看不懂,特别是后面的那个%
  4. % ,表示读取一个字符
  5. % %*[^\n],*表示忽略,[^]表示不是[]字符,合起来的意思就是忽略所有不是\n(回车)的字符,更直接的意思就是忽略到行尾
  6. data = (textscan(fid,'%*[^\n]'));
  7. fclose(fid);
  8. row=length(data{1})
  9. toc

复制代码

row =

      100000

Elapsed time is 12.660186 seconds.

运行时间12s,好像有点长哦,没有想象中的那么厉害。另外这个方法有两个个致命的弱点,因为程序必须每行读取一个字符
(1)假如文件很大,比如10G,那么就算每行读取一个字符,这个也超出了XP内存,因此读取失败!
(2)假如存在空行,那么会将回车读入,%*[^\n]于是就自动忽略了下一行,因此统计的行数不准确!

从上面分析textscan并不适合用于统计大型文件的行数,但是这并不能否定textscan的效率,因为textscan是千真万确一行一行的处理和读取数据文件,只是我们读取第一个字符然后忽略了剩下的所有字符而已。

方法三

其实越是底层的函数效率是越高的,只是使用不方便而已。MATLAB还有一个fread函数,不过这是默认处理的是二进制文件,不过没有关系,文本文件只是一个编码而已,我们还是可以使用fread进行读取的。

  1. tic
  2. fid=fopen('data.csv','rt'); % t是告诉fread是这里文本文件
  3. row=0;
  4. while ~feof(fid)
  5.     % 一次性读取10000字符,计算其中的回车个数,其中10是回车的ASCII编码
  6.     % '*char'表示每次读取一个字符,*表示输出也是字符
  7.     % 放心fread现在已经可以自动识别中文了,万一还是识别不了,
  8.     % 请在fopen中指定文件编码格式,比如gbk
  9.     row=row+sum(fread(fid,10000,'*char')==char(10));
  10.     % 下面还有一个类似的方法,但是效率低很多,大概是上面的一半
  11.     % 'char'表示每次读取一个字符,但是默认输出double,
  12.     % 也就是说读取char然后转换double中间有转换能快吗?
  13.     % row=row+sum(fread(fid,10000,'char')==10);
  14. end
  15. fclose(fid);
  16. row
  17. toc

复制代码

这个效率呱呱的,简直天壤之别呀,才1.7s!看来这个结果比较令我满意哦!

row =

      100000

Elapsed time is 1.721880 seconds.

方法四

上面的方法都是在循环中不停地对文件进行访问,自然效率是高不起来的。对于大型文件,还有其他什么好的解决方案呢,也许这个时候需要借组外部力量了!传说perl语言对文件操作有很多优势,同时linux提供了wc命令对文件进行行统计,不妨试试?

  1. tic
  2. % 判断计算机操作系统
  3. if (isunix) % Linux系统提供了wc命令可以直接使用
  4.     % 使用syetem函数可以执行操作系统的函数
  5.     % 比如window中dir,linux中ls等
  6.     [~, numstr] = system( ['wc -l ', 'data.csv'] );
  7.     row=str2double(numstr);
  8. elseif (ispc) % Windows系统可以使用perl命令
  9.     if exist('countlines.pl','file')~=2
  10.         % perl文件内容很简单就两行
  11.         % while (<>) {};
  12.         % print $.,"\n";
  13.         fid=fopen('countlines.pl','w');
  14.         fprintf(fid,'%s\n%s','while (<>) {};','print $.,"\n";');
  15.         fclose(fid);
  16.     end
  17.     % 执行perl脚本
  18.     row=str2double( perl('countlines.pl', 'data.csv') );
  19. end
  20. row
  21. toc

复制代码

楼主使用的是window系统,调用perl,果然不负众望,才0.89秒

row =

      100000

Elapsed time is 0.889994 seconds.

其实上面的方法在处理真正的大型文件时,还是可能不是很理想的,本文中测试的文件才77M算不上什么大型文件,后来对一个大约80万行,大小622M的csv文件进行测试,使用perl方法结果如下:

row =

      800001

Elapsed time is 15.859564 seconds.

文件越大,计算时间不是简单的线性增加!到了真正几十甚至几百G这样的大型数据,上面的方法几乎是不能忍受的,也许还有更好的解决方法吧!

有人问有什么好的方法生成上面的测试数据呀?我在MATLAB中是这样生成的!

  1. data=rand(10000,100); % 随机生成1w行数据
  2. save data.csv data -ascii % 保存为文本
  3. for ii=1:5 % 自己复制5次,生成2^5=32万行
  4. % 这里使用了dos命令,效率会好些
  5. % 千万不要使用fprint,否你会残废的
  6.    !type data.csv >> data.csv
  7. end

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多