.txt文件中的可变宽度列

  • 本文关键字:文件 txt matlab
  • 更新时间 :
  • 英文 :


我有一个函数,它获取数据并将数据导入到文本文件中。我遇到的问题是格式问题。我希望能够根据列中最宽的字符数组来设置列的宽度。因此,在下面的代码中,我有标签,然后是数据。我的想法是单独计算每个长度,然后找到最大的值。假设第二列标签有15个字符,比任何数据数组都长,那么我想将该列的宽度设置为15+3(空白(,使其为18。如果第3列的数据成员最多有8个字符,那么我想将宽度设置为11。我发现了很多关于固定宽度的文献,我发现我可以做'-*s', *width, colLabels;但我很难弄清楚如何实现这一点。

下面是我的代码,它不会失败,但它需要很长时间,然后因为内存不足而无法打开。我真的试图解决这个问题,但没有成功。

提前感谢,如果我能提供任何其他信息,请告诉我。

for col = 1:length(this.colLabels) % iterate through columns
colLen = length(this.colLabels{col}); % find the longest string in labels
v = max(this.data(:,col)); % find the longest double in data
n = num2str(v, '%.4f');  % precision of 4 after decimal place
dataLen = length(n); 
% find max width for column and add white space
if colLen > dataLen
colWidth = colLen + 3;
else
colWidth = dataLen + 3;
end
% print it
fprintf(fid, '%-*s', this.colWidth, this.colLabels{col}); % write col position i
fprintf(fid, 'n');
fprintf(fid, '%-*s', this.colWidth, this.colUnits{col});% write unit position i
fprintf(fid, 'n');
fprintf(fid, '%-*s', this.colWidth, this.data(:,col)); % write all rows of data in column i
end

有几个地方你会犯错误:

  1. 打印时,数字的大小不一定与其大小有关。考虑1.1234和1000,其中一个是较大的字符串,另一个是更大的数字。这对您的数据可能重要,也可能无关紧要
  2. 二、打印字符串时最好使用正确格式的字符串。%s表示字符串,而不是数字
  3. 也许最重要的是,文本出现在多行上是因为换行符结束了一行,开始了另一行。这意味着您基本上必须一次写一行,而不是一次只写一列

我倾向于在内存中创建文本,然后写入文件。以下不是最干净的实现,但它是有效的。

this.colLabels = {'test' 'cheese' 'variable' 'really long string'};
this.colUnits  = {'ml'   'cm'     'C'        'kg'};
n_columns = length(this.colLabels);
%Fake data
this.data = reshape(1:n_columns*5,5,n_columns);
this.data(1) = 1.2345678;
this.data(5) = 1000; %larger number but smaller string
%Format as desired ...
string_data = arrayfun(@(x) sprintf('%g',x),this.data,'un',0);
string_data = [this.colLabels; this.colUnits; string_data];
%Add on newlines ...
%In newer versions you can use newline instead of char(10)
string_data(:,end+1) = {char(10)};
string_lengths = cellfun('length',string_data);
max_col_widths = max(string_lengths,[],1);
%In newer versions you can use singleton expansion, but beware
n_spaces_add = bsxfun(@minus,max_col_widths,string_lengths);
%left justify filling with spaces
final_strings = cellfun(@(x,y) [x blanks(y)],string_data,num2cell(n_spaces_add),'un',0);
%Optional delimiter between columns
%Don't add delimiter for last column or for newline column
final_strings(:,1:end-2) = cellfun(@(x) [x ', '],final_strings(:,1:end-2),'un',0);
%Let's skip last newline
final_strings{end,end} = '';
%transpose for next line so that (:) goes by row first, not column
%Normally (:) linearizes by column first
final_strings = final_strings';
%concatenate all cells together
entire_string = [final_strings{:}];
%Write this to disk fprintf(fid,'%s',entire_string);

文本文件中的数据是逐行存储的,因此不能逐列写入。您需要首先确定列的宽度并写入标签/单元标题,然后写入所有数据。我们所需要的只是fprintf的正确格式字符串:固定宽度格式,fprintf对于导出以列分隔的数据非常有用。

代码的第一部分可以确定列的宽度(假设数据只有正样本(。您只需要将其存储在一个数组中。

nCol=length(this.colLabels);
colWidth = zeros(1,nCol);
for col = 1:nCol
colLen = length(this.colLabels{col}); % find the longest string in labels
v = max(this.data(:,col)); % find the longest double in data
n = num2str(v, '%.4f');  % precision of 4 after decimal place
dataLen = length(n); 
% find max width for column and add white space
colWidth(col)=max(colLen,dataLen);
end

现在,我们需要为标签和数据构建格式字符串,以便与sprintf一起使用。标题的格式字符串将类似于'%6s %8s %10sn',数据的格式字符串为'%6.4f %8.4f %10.4fn'

fmtHeader=sprintf('%%%ds   ',colWidth);
fmtData=sprintf('%%%d.4f   ',colWidth);
%Trim the triple space at the end and add the newline
fmtHeader=[fmtHeader(1:end-3) 'n'];
fmtData  =[fmtData(1:end-3) 'n'];

我们使用这样一个事实,即当sprintf被赋予一个数组作为输入时,它将遍历所有值以生成一个长字符串。我们可以使用相同的技巧来写入数据,但如果我们逐行写入,并且Matlab按列主顺序存储数据,则需要转置。

fid=fopen('myFile.txt');
fprintf(fid,fmtHeader,this.colLabels{:});
fprintf(fid,fmtHeader,this.colUnits{:});
fprintf(fid,fmtData,transpose(this.data));
fclose(fid);

对于标题,可以将单元格转换为用{:}分隔的逗号列表。这与写入fprintf(fid,fmtHeader,this.colLabels{1},this.colLabels{2},...)相同

使用来自@Jimbo的答案和fid=1;的相同测试数据将fprintf输出到屏幕,代码给出:

test    cheese   variable   really long string
ml        cm          C                   kg
1.2346    6.0000    11.0000              16.0000
2.0000    7.0000    12.0000              17.0000
3.0000    8.0000    13.0000              18.0000
4.0000    9.0000    14.0000              19.0000
1000.0000   10.0000    15.0000              20.0000

最后,最紧凑的代码版本是:

fid=1; %print to screen for test purpose
colWidth =max( cellfun(@length,this.colLabels(:)') , max(1+floor(log10(max(this.data,[],1))) , 1) + 5); %log10 to count digits, +5 for the dot and decimal digits ; works for data >=0 only
fprintf(fid,[sprintf('%%%ds   ',colWidth(1:end-1)) sprintf('%%%dsn',colWidth(end))],this.colLabels{:},this.colUnits{:}); %print header
fprintf(fid,[sprintf('%%%d.4f   ',colWidth(1:end-1)) sprintf('%%%d.4fn',colWidth(end))],this.data'); %print data

相关内容

  • 没有找到相关文章

最新更新