Столбцы переменной ширины в TXT-файлах - PullRequest
0 голосов
/ 31 октября 2018

У меня есть функция, которая берет данные и импортирует эти данные в текстовый файл. У меня проблема с форматированием. Я хочу иметь возможность установить ширину столбцов на основе самого широкого массива символов в этом столбце. Итак, в приведенном ниже коде у меня есть метки, а затем данные. Моя идея заключается в том, чтобы взять длину каждого в отдельности и найти наибольшую ценность. Скажем, у меток второго столбца 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 голосов
/ 31 октября 2018

Данные в текстовом файле хранятся одна строка за другой, поэтому вы не можете писать столбец за столбцом. Сначала необходимо определить ширину столбцов и написать заголовок метки / единицы измерения, а затем записать все данные. Все, что нам нужно, это правильная строка формата для 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
0 голосов
/ 31 октября 2018

Есть несколько мест, где вы делаете ошибки:

  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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...