📄 tffio.m
字号:
function [varargout] = tffio(varargin)
% tffio - read/write binary file with TFF spec (and content)
%
% FORMAT: tffcont = tffio(filename, tffspec [, options])
% OR tffio(wfilename, tffspec, tffcont [, options])
%
% Input fields:
%
% filename filename of binary file to read
% tffspec either filename or content or spec struct of TFF
% options optional arguments, one of
% 'verbose', 'v' or '-v' (for verbose reading/writing)
% 'force', 'f', '-f' (for returning partial content)
%
% wfilename filename of binary file to write
% tffcont file content (struct)
%
% Output fields:
%
% tffcont file contents struct (depending on TFF)
%
% See also tffdocu, tffparse
% Version: v0.7a
% Build: 7080111
% Date: Aug-01 2007, 11:54 AM CEST
% Author: Jochen Weber, Brain Innovation, B.V., Maastricht, NL
% URL/Info: http://wiki.brainvoyager.com/BVQXtools
tffversion = 'v0.7a';
% argument check
if nargin < 2 || ...
~ischar(varargin{1}) || ...
isempty(varargin{1}) || ...
(~isstruct(varargin{2}) && ~ischar(varargin{2})) || ...
isempty(varargin{2})
error( ...
'BVQXtools:BadArgument', ...
'Bad or missing argument for tffio.' ...
);
end
filename = varargin{1}(:)';
tffspec = varargin{2}(:)';
tffcont = [];
% default options
forcemode = false;
verbosemode = false;
writemode = false;
% tff content structure given
if nargin > 2 && ...
isstruct(varargin{3}) && ...
~isempty(varargin{3})
tffcont = varargin{3};
writemode = true;
end
% check additional arguments
if nargin > 2
for argc = 3:nargin
% what is given
argv = varargin{argc};
% character argument
if strcmpi(class(argv), 'char')
% known argument
switch lower(argv)
% force mode
case {'f', 'force', '-f'}
forcemode = true;
% verbose mode
case {'v', 'verbose', '-v'}
verbosemode = true;
end
% discard other arguments/options
end
end
end
% TFF is a valid SPEC struct
if isstruct(tffspec) && ...
~isempty(tffspec) && ...
isfield(tffspec, 'ArrayFormat') && ...
ischar(tffspec.ArrayFormat) && ...
any(tffspec.ArrayFormat(:) == '%') && ...
isfield(tffspec, 'CustomDelimiters') && ...
iscell(tffspec.CustomDelimiters) && ...
isfield(tffspec, 'Extensions') && ...
iscell(tffspec.Extensions) && ...
isfield(tffspec, 'FieldDelimCollapse') && ...
~isempty(tffspec.FieldDelimCollapse) && ...
islogical(tffspec.FieldDelimCollapse) && ...
isfield(tffspec, 'FieldDelimiters') && ...
iscell(tffspec.FieldDelimiters) && ...
~isempty(tffspec.FieldDelimiters) && ...
isfield(tffspec, 'LineDelimiters') && ...
iscell(tffspec.LineDelimiters) && ...
~isempty(tffspec.LineDelimiters) && ...
isfield(tffspec, 'ListOfFields') && ...
isstruct(tffspec.ListOfFields) && ...
isfield(tffspec, 'Loops') && ...
isstruct(tffspec.Loops) && ...
isfield(tffspec, 'Magic') && ...
isstruct(tffspec.Magic) && ...
isfield(tffspec, 'MaxFieldNameLength') && ...
isnumeric(tffspec.MaxFieldNameLength) && ...
~isempty(tffspec.MaxFieldNameLength) && ...
isfield(tffspec, 'ParagraphArrays') && ...
~isempty(tffspec.ParagraphArrays) && ...
islogical(tffspec.ParagraphArrays) && ...
isfield(tffspec, 'SkipEmptyLines') && ...
~isempty(tffspec.SkipEmptyLines) && ...
islogical(tffspec.SkipEmptyLines) && ...
isfield(tffspec, 'Variables') && ...
isstruct(tffspec.Variables)
% any other struct is rejected
elseif isstruct(tffspec)
error( ...
'BVQXtools:BadArgument', ...
'Invalid struct TFF specification.' ...
);
% for character arrays
elseif ischar(tffspec)
% valid file contents (0x0a/0x0d delimited)
if any(tffspec == char(10) | tffspec == char(13)) || ...
exist(tffspec, 'file') == 2
% parse content
try
if writemode
tffcont = tffio( ...
filename, ...
tffparse(tffspec), ...
varargin{:});
else
tffcont = tffio( ...
filename, ...
tffparse(tffspec), ...
varargin{:});
end
if ~writemode
varargout{1} = tffcont;
else
if nargout > 0
varargout = cell(1, nargout);
end
end
return;
catch
rethrow(lasterror);
end
end
% otherwise
error( ...
'BVQXtools:BadArgument', ...
'Invalid tffspec argument given.' ...
);
end
% get shortcuts for specs
arf = tffspec.ArrayFormat;
fds = tffspec.FieldDelimiters;
ldc = double(tffspec.SkipEmptyLines(1));
lds = tffspec.LineDelimiters;
mfl = num2str(abs(floor(tffspec.MaxFieldNameLength(1))) + 1);
par = tffspec.ParagraphArrays(1);
% check specs
if isempty(regexpi(mfl, '^\d+$'))
error( ...
'BVQXtools:BadTFFSpec', ...
'Invalid MaxFieldNameLength content.' ...
);
end
% initialize rules variables
rule = tffspec.ListOfFields;
rules = length(rule);
% get loops shortcut and prepare loopi struct, loopx
loops = tffspec.Loops;
loopf = fieldnames(loops);
if ~isempty(loopf)
loopf = fieldnames(loops.(loopf{1}));
loopi = cell2struct(cell(0, 0, length(loopf)), loopf, 3);
else
loopi = struct;
end
loopx = {};
% get file extension
[fnamex{1:3}] = fileparts(filename);
fnamex = fnamex{3};
fnamex(fnamex == '.') = [];
% initialize output
if isempty(tffcont)
tffcont = struct;
end
% initialize internal variables
namevars = struct;
namevars.TFFVERSION = tffversion;
namevars.TFFPATH = fileparts(mfilename('fullpath'));
namevars.TFFREAD = ~writemode;
namevars.TFFWRITE = writemode;
namevars.FILENAME = filename;
namevars.EXTENSION = fnamex;
namevars.NEXTLINE = 1;
% reading TFF content
if ~writemode
% read entire file into 1xN char array
try
textcont = asciiread(filename);
catch
error( ...
'BVQXtools:FileNotReadable', ...
'File not readable: ''%s''.', ...
filename ...
);
end
% detect line delimiter
for ldsc = 1:length(lds)
if ~isempty(strfind(textcont, lds{ldsc}))
lds = lds{ldsc};
break;
end
end
% no line delimiter found
if ~ischar(lds)
error( ...
'BVQXtools:BadTFFCont', ...
'No specified line delimiter detected.' ...
);
end
% split to lines
linecont = splittocell(textcont, lds, ldc);
if ldc && ...
~isempty(linecont) && ...
isempty(linecont{1})
linecont(1) = [];
end
linecont = linecont(:);
linecount = length(linecont);
% get file "size"
namevars.LINECOUNT = linecount;
% writing TFF content
else
% BeforeWriteCode ?
if ~isempty(tffspec.BeforeWriteCode)
try
eval(tff_parsecode(tffspec.BeforeWriteCode(:)'));
catch
error( ...
'BVQXtools:EvaluationError', ...
'Error raised by BeforeWriteCode: ''%s''.', ...
lasterr ...
);
end
end
% create empty variables
linecont = {};
linecount = 0;
% get standard variables
fds = fds{1};
lds = lds{1};
end
% make sure to parse code only once
for rulec = 1:rules
prule = rule(rulec);
prule.cond = tff_parsecode(prule.cond);
prule.dim = tff_parsecode(prule.dim);
prule.varname = tff_parsecode(prule.varname);
rule(rulec) = prule;
end
% grand loop
linec = 1; % number of next line in file
rulec = 1; % pointer to next rule to process
loopc = 0; % counter of loops (in which loop are we)
% try used for forcemode (on indentation !!!)
try % forcemode
while rulec <= rules
% get shortcut to current rule to process (and subfields)
prule = rule(rulec);
rtype = lower(prule.type);
rfldn = prule.field;
rdata = lower(prule.datatype);
rcond = prule.cond;
rdim = prule.dim;
rexpr = prule.varname;
rform = prule.format;
% try to resolve dim
if isempty(rdim)
rdim = 1;
else
try
eval(['rdim=double([' rdim ']);']);
if isempty(rdim) || ...
size(rdim, 2) > 2
error('BADDIM');
end
catch
error( ...
'BVQXtools:BadExpression', ...
'Bad DIM expression: ''%s''.', ...
prule.dim ...
);
end
end
% special case, flist & write is done by field!
if writemode && ...
strcmp(rtype, 'flist')
rtype = 'field';
end
% what to do
switch (rtype)
% array
case {'array'}
% check cond
if ~isempty(rcond)
try
eval(['iofield=false;if ' rcond ',iofield=true;end']);
catch
error( ...
'BVQXtools:BadExpression', ...
'Couldn''t evaluation COND expression: ''%s''.', ...
rcond ...
);
end
if ~iofield
rulec = rulec + 1;
continue;
end
end
% check dim
if length(rdim) ~= 2
error( ...
'BVQXtools:BadTFFSpec', ...
'Dim for ARRAY must be 2-D.' ...
);
end
rdimrows = rdim(1);
rdimcols = rdim(2);
% read access
if ~writemode
% initialize array
rarray = zeros(rdim);
% skip paragraphing line?
if par && ...
ldc == 0
linec = lice + 1;
end
% skip empty column paragraph
if rdimcols == 0 && ...
ldc
rdimrows = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -