function theStructs = ReadStructsFromText(filename) % theStructs = ReadStructsFromText(filename) % % Open a tab delimited text file. The first row should % contain the field names for a structure. Each following % row contains the data for one instance of that structure. % % This routine reads each row and returns an array of structures, % one struct for each row, with the data filled in. Data % can be numeric or string for each field. % % Not a lot of checking is done for cases where the read file % fails to conform to the necessary format. % % See Also: WriteStructsToText % 6/15/03 dhb Wrote it. % 07/01/03 dhb Support string as well as numeric data. % 07/02/03 dhb, jg Handle white space in column headers. % 07/03/03 dhb More little tweaks. % 08/06/03 dhb Handle fgetl returns empty string. % 08/22/07 dhb This was modified on disk but not commented our uploaded to SVN repository. % 4/26/12 dhb Squeeze '/' out of field names too. % 5/31/12 dhb Squeeze '*' out of field names too. % 6/7/13 dhb Suppress uninteresting warning on str2num. % 4/3/14 dhb Try to handle NaN in text files. Worked for at least one case. % Open the file fid = fopen(filename); if fid == -1 error('Cannot open file %s', filename); end % Read first line to get field names for returned structure theFields = {}; firstLine = fgetl(fid); theIndex = 1; i = 1; while (1) wholeField = ''; while (1) readString = firstLine(theIndex:end); [field,count,nil,nextIndex] = sscanf(readString,'%s',1); if (count == 0) break; end wholeField = [wholeField field]; theIndex = theIndex+nextIndex-1; if (nextIndex <= length(readString) && abs(readString(nextIndex)) == 9) break; else wholeField = [wholeField ' ']; end end if (count == 0) if (~isempty(wholeField)) theFields{i} = wholeField; i = i+1; end break; end theFields{i} = wholeField; wholeField = []; i = i+1; end nFields = length(theFields); % Squeeze white space out of each field for i = 1:nFields newField = ''; oldField = theFields{i}; for j = 1:length(oldField) if (~isspace(oldField(j)) && oldField(j) ~= '.' && oldField(j) ~= '/' && oldField(j) ~= '*') newField = [newField oldField(j)]; end end theFields{i} = newField; end % Octave doesn't support the textscan function, so we use the old method % of extracting data for Octave users. The new method allows spaces in % strings. if ~IsOctave % Read out all the data from the text file delimited by newline % characters. data = textscan(fid, '%s', 'delimiter', '\n'); data = data{1}; % Read the values from each line of the text and convert them to % doubles if possible. f = 1; for i = 1:size(data, 1) values = textscan(data{i}, '%s', 'delimiter', '\t'); values = values{1}; for j = 1:size(values, 1) convertedValue = []; % Convert the entry from a string to a number if possible. We % first check to see if the value is on the path as a function % because the str2num function calls eval on its input which % will cause it to execute. if (strcmp(lower(values{j}),'nan')) convertedValue = NaN; elseif isempty(which(values{j})) && isempty(which(strtok(values{j}))) oldWarn = warning('off','MATLAB:namelengthmaxexceeded'); convertedValue = str2num(values{j}); %#ok warning(oldWarn.state,'MATLAB:namelengthmaxexceeded'); end % If the value successfully converted, overwrite what was % already in the cell array. if ~isempty(convertedValue) values{j} = convertedValue; end end theStructs(f) = cell2struct(values, theFields, 1); %#ok f = f + 1; end else % Now read lines and pull out structure elements f = 1; while (1) theLine = fgetl(fid); if (isempty(theLine) || theLine == -1) break; end theIndex = 1; theData = cell(nFields,1); for i = 1:nFields readString = theLine(theIndex:end); [field,count,nil,nextIndex] = sscanf(readString,'%g',1); if (count == 0) [field,count,nil,nextIndex] = sscanf(readString,'%s',1); if (count == 0) error('Cannot parse input'); end end theIndex = theIndex+nextIndex-1; theData{i} = field; end theStruct = cell2struct(theData,theFields,1); theStructs(f) = theStruct; f = f+1; end end % If there was no data in the file, return an empty matrix. if ~exist('theStructs', 'var') theStructs = []; end % Close the file. fclose(fid);