我有一个获取数据并将该数据导入文本文件的功能。我遇到的问题是格式化。我希望能够根据该列中最宽的字符集来设置列的宽度。因此,在下面的代码中,我有标签,然后有数据。我的想法是单独计算每个长度并找到最大值。假设第二列标签有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
答案 0 :(得分:2)
在少数地方您会犯错误:
%s
用于字符串,而不是数字。我倾向于在内存中创建文本,然后再写入文件。以下不是最干净的实现,但可以运行。
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);
答案 1 :(得分:2)
文本文件中的数据一行一行地存储,因此您不能逐列写入。您首先需要确定列的宽度并写入标签/单元标题,然后再写入所有数据。我们所需要做的就是为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 %10s\n'
的标题,'%6.4f %8.4f %10.4f\n'
的数据。
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('%%%ds\n',colWidth(end))],this.colLabels{:},this.colUnits{:}); %print header
fprintf(fid,[sprintf('%%%d.4f ',colWidth(1:end-1)) sprintf('%%%d.4f\n',colWidth(end))],this.data'); %print data