.txt文件中的可变宽度列

时间:2018-10-30 22:50:17

标签: 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

2 个答案:

答案 0 :(得分:2)

在少数地方您会犯错误:

  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);

答案 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