gusucode.com > datatypes 工具箱matlab源码程序 > datatypes/@tabular/writeXLSFile.m
function writeXLSFile(t,filename,ext,args) %WRITEXLSFILE Write a table to an Excel spreadsheet file. % Copyright 2012-2016 The MathWorks, Inc. [writeVarNames,writeRowNames,sheetName,range,locale] = getArgs(t,args); book = createWorkbook(filename, ext, true); if book.AreDates1904 dateOrigin = '1904'; else dateOrigin = '1900'; end sheetObj = getSheetFromBook(book, sheetName); [row1, col1, row2, col2, truncateColumns, truncateRows] = parseRange(range, sheetObj, t); % Determine the maximum number of columns we can write if truncateColumns maxCol = col2; else maxCol = Inf; end % How many rows are we supposed to write. if truncateRows && (row2 - row1 + 1 <= t.rowDim.length) dataHeight = row2 - row1 + 1 - writeVarNames; else dataHeight = t.rowDim.length; end vars = cell(dataHeight + writeVarNames, 0); % Write row names. if writeRowNames rownames = t.rowDim.labels(1:dataHeight); if writeVarNames rownames = [t.metaDim.labels{1}; rownames]; end vars = [vars rownames]; end import matlab.internal.tableUtils.matricize cells = {}; colCount = col1; for j = 1:t.varDim.length if colCount > maxCol, break; end varnamej = t.varDim.labels{j}; if dataHeight > 0 varj = t.data{j}; varj = extractVarChunk(varj, 1, dataHeight); if iscell(varj) % xlswrite cannot write out non-scalar-valued cells -- convert cell % variables to a cell of the appropriate width containing only % scalars. [~,ncols] = size(varj); % Treat N-D as 2-D ncellColsj = max(cellfun(@ncolsCell,matricize(varj)),[],1); newNumCols = sum(ncellColsj); newVarj = cell(dataHeight,newNumCols); % Expand out each column of varj into as many columns as needed to % have only scalar-valued cells, possibly padded with empty cells. cnt = 0; for jj = 1:ncols varjj = varj(:,jj); num = ncellColsj(jj); newVarjj = cell(dataHeight,num); for i = 1:dataHeight % Expand each cell with non-scalar contents into a row of cells containing scalars varjj_i = varjj{i}; if ischar(varjj_i) % Put each string into its own cell. If there are no % strings (zero rows or zero pages in the original char % array), the output will be a single empty cell. vals = char2cell(varjj_i); % creates a 2-D cellstr if isempty(vals), vals = {''}; end elseif isstring(varjj_i) vals = num2cell(varjj_i); elseif isnumeric(varjj_i) if isreal(varjj_i) vals = num2cell(varjj_i); else vals = num2cell(real(varjj_i)); end elseif islogical(varjj_i) vals = num2cell(varjj_i); elseif isa(varjj_i,'categorical') vals = strrep(cellstr(varjj_i),'<undefined>',''); elseif isa(varjj_i,'duration') || isa(varjj_i,'calendarDuration') vals = cellstr(varjj_i,[],locale); elseif isa(varjj_i,'datetime') if any(exceltime(varjj_i, dateOrigin) < 0) vals = cellstr(varjj_i,[],locale); else % datetimes are represented as complex numbers in C++. The % signals libmwspreadsheet that the data is a datetime and % should be treated as such. vals = arrayfun(@(x){x},complex(exceltime(varjj_i),0)); end else vals = cell(0,0); % write out only an empty cell end newVarjj(i,1:numel(vals)) = vals(:)'; end newVarj(:,cnt+(1:num)) = newVarjj; cnt = cnt + num; end varj = newVarj; else % xlswrite will convert any input to cell array anyway, may as well do % it here in all cases to get correct behavior for character and for % cases xlswrite won't handle. if ischar(varj) varj = char2cell(varj); elseif isstring(varj) varj = num2cell(varj); elseif isnumeric(varj) if isreal(varj) varj = num2cell(varj); else varj = num2cell(real(varj)); end elseif islogical(varj) varj = num2cell(matricize(varj)); elseif isa(varj,'categorical') varj = strrep(cellstr(matricize(varj)),'<undefined>',''); elseif isa(varj,'duration') || isa(varj,'calendarDuration') varj = cellstr(matricize(varj),[],locale); elseif isa(varj,'datetime') if any(exceltime(varj, dateOrigin) < 0) varj = cellstr(matricize(varj),[],locale); else % datetimes are represented as complex numbers in C++. The % signals libmwspreadsheet that the data is a datetime and % should be treated as such. varj = arrayfun(@(x){x},complex(exceltime(varj),0)); end else % write out empty cells varj = cell(dataHeight,1); end end else varj = cell(0, 1); end [~,ncols] = size(varj); % Treat N-D as 2-D if writeVarNames if ncols > 1 varj = [strcat({varnamej},'_',num2str((1:ncols)'))'; varj]; %#ok<AGROW> else varj = [{varnamej}; varj]; %#ok<AGROW> end end vars = [vars varj]; %#ok<AGROW> colCount = colCount + ncols; cells = [cells vars]; %#ok<AGROW> vars = cell(dataHeight + writeVarNames,0); end % in case matricizing grew it beyond the bounds we care to write [~, numCols] = size(cells); if truncateColumns endCol = min(col2 - col1 + 1, numCols); cells = cells(:,1:endCol); end % validate the range by trying to get the range to write to. if it is % beyonds the edges, the call to getRange() will throw try [hght, wdth] = size(cells); writeRng = [row1, col1, hght, wdth]; sheetObj.getRange(writeRng); catch% only one thing could go wrong, we are using a numeric range throwTooBigForFormatError(book.Format, writeRng); end % Prior to writing, unmerge all the cells in the range % Writing to merged cells will drop the 2nd->Nth items in the range % without error. sheetObj.unmerge(writeRng); try sheetObj.write(cells, writeRng); catch ME if strcmp(ME.identifier, 'MATLAB:spreadsheet:sheet:protectedOrFinal') error(message('MATLAB:table:write:ProtectedOrFinal', sheetObj.Name, filename)); else throw(ME); end end sheetObj.autoFitColumns(writeRng); try book.save(filename); catch ME if ispc && strcmp(ME.identifier, 'MATLAB:spreadsheet:book:save') ... && exist(filename, 'file') error(message('MATLAB:table:write:FileOpenInAnotherProcess', filename)); else throw(ME); end end end % writeXLSFile function %-------------------------------------------------------------------------- function [writeVarNames,writeRowNames,sheet,range,locale] = getArgs(t,args) import matlab.internal.tableUtils.validateLogical pnames = {'WriteVariableNames' 'WriteRowNames' 'Sheet' 'Range' 'DateLocale'}; dflts = { true false 1 'A1' 'system'}; [writeVarNames,writeRowNames,sheet,range,locale] ... = matlab.internal.table.parseArgs(pnames, dflts, args{:}); writeRowNames = validateLogical(writeRowNames,'WriteRowNames'); writeVarNames = validateLogical(writeVarNames,'WriteVariableNames'); % Only write row names if asked to, and if they exist. writeRowNames = (writeRowNames && t.rowDim.hasLabels); end %-------------------------------------------------------------------------- function sheetObj = getSheetFromBook(book, sheet) try sheetObj = book.getSheet(sheet); catch ME if strcmp(ME.identifier, 'MATLAB:spreadsheet:book:openSheetName') % Add the sheet with the name provided. sheetObj = book.addSheet(sheet); elseif strcmp(ME.identifier, 'MATLAB:spreadsheet:book:openSheetIndex') % Add blank sheets leading up to the index specified. [~, nSheets] = size(book.SheetNames); blanksToAdd = sheet - nSheets - 1; % If blanksToAdd <= 0, we do nothing for i = 1:blanksToAdd sheetNum = nSheets + i; book.addSheet(['Sheet' num2str(sheetNum)], sheetNum); end % Add a new sheet using Excel's sheet naming convention at the % specified index. sheetObj = book.addSheet(['Sheet' num2str(sheet)], sheet); else rethrow(ME); end % Use the same warning xlswrite does. warning(message('MATLAB:xlswrite:AddSheet')); end end %-------------------------------------------------------------------------- function [row1, col1, row2, col2, truncateColumns, truncateRows] = parseRange(range, sheet, t) % Transform the range into one we can parse using the spreadsheet % library. try if ~ischar(range) % Only accept non-numeric ranges error(message('MATLAB:table:write:InvalidRange')); end % Get the numeric representation of the range, and the range type. [numRange, rangetype] = sheet.getRange(range, false); % Assign output variables. row1 = numRange(1); col1 = numRange(2); row2 = row1 + numRange(3) - 1; col2 = col1 + numRange(4) - 1; switch rangetype case {'two-corner', 'named'} truncateColumns = true; truncateRows = true; case 'single-cell' truncateColumns = false; truncateRows = false; case 'column-only' row1 = 1; row2 = t.rowDim.length; truncateColumns = true; truncateRows = false; case 'row-only' col1 = 1; col2 = t.varDim.length; truncateColumns = false; truncateRows = true; otherwise truncateColumns = true; truncateRows = true; end catch ME if strcmp(ME.identifier, 'MATLAB:spreadsheet:sheet:rangeParseInvalid') % Throw our own range validation error. error(message('MATLAB:table:write:InvalidRange')); else % If we get an unexpected error, rethrow it. rethrow(ME); end end end %-------------------------------------------------------------------------- function book = createWorkbook(filename, ext, interactive) % If the workbook exists, open it. Otherwise create a new workbook. if exist(filename, 'file') try book = matlab.io.spreadsheet.internal.createWorkbook(ext, filename, interactive); catch ME if strcmp(ME.identifier, 'MATLAB:spreadsheet:book:fileOpen') % The file exists but is invalid or encrypted with a password. error(message('MATLAB:table:write:CorruptOrEncrypted', filename)); else throw ME; end end else % The file doesn't exist so we need to create one. book = matlab.io.spreadsheet.internal.createWorkbook(ext, [], interactive); % Create the default second and third sheets that Excel gives us. book.addSheet('Sheet2', 2); book.addSheet('Sheet3', 3); end end %-------------------------------------------------------------------------- function m = ncolsCell(c) % How many columns will be needed to write out the contents of a cell? if ischar(c) % Treat each row as a separate string, including rows in higher dims. [n,~,d] = size(c); % Each string gets one "column". Zero rows (no strings) gets a single % column to contain the empty string, even for N-D,. In particular, % '' gets one column. m = max(n*d,1); elseif isnumeric(c) || islogical(c) || isa(c,'categorical') m = max(numel(c),1); % always write out at least one empty field else m = 1; % other types are written as an empty field end end %-------------------------------------------------------------------------- function cs = char2cell(c) % Convert a char array to a cell array of strings, each cell containing a % single string. Treat each row as a separate string, including rows in % higher dims. % Create a cellstr array the same size as the original char array (ignoring % columns), except with trailing dimensions collapsed down to 2-D. [n,~,d] = size(c); szOut = [n,d]; if isempty(c) % cellstr would converts any empty char to {''}. Instead, preserve the % desired size. cs = repmat({''},szOut); else % cellstr does not accept N-D char arrays, put pages as more rows. if ~ismatrix(c) c = permute(c,[2 1 3:ndims(c)]); c = reshape(c,size(c,1),[])'; end cs = reshape(num2cell(c,2),szOut); end end %-------------------------------------------------------------------------- function varChunk = extractVarChunk(var, rowStart, rowFinish) if ischar(var) % Turn ND char array into 3D varChunk = var(rowStart:rowFinish, :, :); % 'Matricize' 3D char into 2D [n,m,d] = size(varChunk); if d > 1 varChunk = permute(varChunk,[1 3:ndims(varChunk) 2]); varChunk = reshape(varChunk,[n*d,m]); end varChunk = reshape(num2cell(varChunk,2), [n d]); else % 2D indexing automatically 'matricize' ND non-char arrays varChunk = var(rowStart:rowFinish, :); end end %-------------------------------------------------------------------------- function throwTooBigForFormatError(fmt, writerng) % We only care about the size limits of the given format. We don't want % to start Excel, so if the format is XLSB, use XLSX since they are the % same size. if strcmpi(fmt, 'xlsb') fmt = 'xlsx'; end % create a non-interactive book b = matlab.io.spreadsheet.internal.createWorkbook(fmt, [], false); s = b.getSheet(1); maxColsRange = s.getRange('1:1', false); maxRowsRange = s.getRange('A:A', false); maxrange = [maxRowsRange(3), maxColsRange(4)]; writerngRC = writerng(1:2) + writerng(3:4) - 1; exceedsByRC = max(writerngRC - maxrange, [0 0]); writeStartCell = s.getRange([writerng(1) writerng(2) 1 1]); error(message('MATLAB:table:write:DataExceedsSheetBounds', writeStartCell, exceedsByRC(1), exceedsByRC(2))); end