PageRenderTime 29ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/core/third_parties/inifile.m

http://brainstream.googlecode.com/
MATLAB | 903 lines | 560 code | 55 blank | 288 comment | 87 complexity | 8775f5f077011640ff77cdbba3f23cd4 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, LGPL-2.0, GPL-3.0, GPL-2.0, LGPL-2.1, LGPL-3.0, BSD-2-Clause
  1. function varargout = inifile(varargin)
  2. %INIFILE Creates, reads, or writes data from/to a standard ini (ascii)
  3. % file. Such a file is organized into sections
  4. % ([section name]), subsections(enclosed by {subsection name}),
  5. % and keys (key=value). Empty lines and lines with the first non-empty
  6. % character being ; (comment lines) are ignored.
  7. %
  8. % Usage:
  9. % INIFILE(fileName,'new')
  10. % Rewrites an existing file - creates a new, empty file.
  11. %
  12. % INIFILE(fileName,'write',keys,<style>)
  13. % Writes keys given as cell array of strings (see description of
  14. % the keys below). Optional style variable: 'tabbed' writes sections,
  15. % subsections and keys in a tabbed style to get more readable
  16. % file. The 'plain' style is the default style. This only affects
  17. % the keys that will be written/rewritten.
  18. %
  19. % INIFILE(fileName,'deletekeys',keys)
  20. % Deletes keys and their values - if they exist.
  21. %
  22. % [readsett,result] = INIFILE(fileName,'read',keys)
  23. % Reads the values of the keys where readsett is a cell array of
  24. % strings and/or numeric values of the keys. If any of the keys
  25. % is not found, the default value is returned (if given in the
  26. % 5-th column of the keys parameter). result is a cell array of
  27. % strings - one for each key read; empty if OK, error/warning
  28. % string if error; in both cases an empty string is returned in
  29. % readsett{i} for the i-th key if error.
  30. %
  31. % [keys,sections,subsections] = INIFILE(fName,'readall')
  32. % Reads entire file and returns all the sections, subsections
  33. % and keys found.
  34. %
  35. %
  36. % Notes on the keys cell array given as an input parameter:
  37. % Cell array of STRINGS; either 3, 4, or 5 columns.
  38. % Each row has the same number of columns. The columns are:
  39. % 'section': section name string (the root is considered if
  40. % empty)
  41. % 'subsection': subsection name string (the root is considered
  42. % if empty)
  43. % 'key': name of the field to write/read from (given as
  44. % a string).
  45. % value: (optional) STRING or NUMERIC value (scalar or
  46. % matrix) to be written to the
  47. % ini file in the case of 'write' operation OR
  48. % conversion CHAR for read operation:
  49. % 'i' for integer, 'd' for double, 's' or
  50. % '' or not given for string (default).
  51. % defaultValue: (optional) string or numeric value (scalar or
  52. % matrix) that is returned when the key is not
  53. % found or an empty value is found
  54. % when reading ('read' operation).
  55. % If the defaultValue is not given and the key
  56. % is not found, an empty value is returned.
  57. % It MUST be in the format as given by the
  58. % value, e.g. if the value = 'i' it must be
  59. % given as an integer etc.
  60. %
  61. %
  62. % EXAMPLE:
  63. % Suppose we want a new ini file, test1.ini with 4 fields, including a
  64. % 5x5 matrix (see below). We can write the 5 fields into the ini file
  65. % using:
  66. %
  67. % x = rand(5); % matrix data
  68. % inifile('test1.ini','new');
  69. % writeKeys = {'measurement','person','name','Primoz Cermelj';...
  70. % 'measurement','protocol','id',1;...
  71. % 'application','','description.m1','some...';...
  72. % 'application','','description.m2','some...';...
  73. % 'data','','x',x};
  74. % inifile('test1.ini','write',writeKeys,'plain');
  75. %
  76. % Later, you can read them out. Additionally, if any of them won't
  77. % exist, a default value will be returned (if the 5-th column is given
  78. % for all the rows as below).
  79. %
  80. % readKeys = {'measurement','person','name','','John Doe';...
  81. % 'measurement','protocol','id','i',0;...
  82. % 'application','','description.m1','','none';...
  83. % 'application','','description.m2','','none';...
  84. % 'data','','x','d',zeros(5)};
  85. % readSett = inifile('test1.ini','read',readKeys);
  86. %
  87. % Or, we can just read all the keys out
  88. % [keys,sections,subsections] = inifile(test1.ini,'readall');
  89. %
  90. %
  91. % NOTES: If the operation is 'write' and the file is empty or does not
  92. % exist, a new file is created. When writing and if any of the section
  93. % or subsection or key does not exist, it creates (adds) a new one.
  94. % Everything but value is NOT case sensitive. Given keys and values
  95. % will be trimmed (leading and trailing spaces will be removed).
  96. % Any duplicates (section, subsection, and keys) are ignored. Empty
  97. % section and/or subsection can be given as an empty string, '',
  98. % but NOT as an empty matrix, [].
  99. %
  100. % Numeric matrices can be represented as strings in one of the two form:
  101. % '1 2 3;4 5 6' or '1,2,3;4,5,6' (an example).
  102. %
  103. % Comment lines starts with ; as the first non-empty character but
  104. % comments can not exist as a tail to a standard, non-comment line as ;
  105. % is also used as a row delimiter for matrices.
  106. %
  107. % This function was tested on the win32 platform only but it should
  108. % also work on Unix/Linux platforms. Since some short-circuit operators
  109. % are used, at least Matlab 6.5 (R13) is required.
  110. %
  111. %
  112. % FREE SOFTWARE - please refer the source
  113. % Copyright (c) 2003-2005 by Primoz Cermelj
  114. % First release on 29.01.2003
  115. % Primoz Cermelj, Slovenia
  116. % Contact: primoz.cermelj@email.si
  117. % Download location: http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=2976&objectType=file
  118. %
  119. % Version: 1.4.2
  120. % Last revision: 12.01.2007
  121. %
  122. % Bug reports, questions, etc. can be sent to the e-mail given above.
  123. %
  124. % This programme is free software; you can redistribute it and/or
  125. % modify it under the terms of the GNU General Public License
  126. % as published by the Free Software Foundation; either version 2
  127. % of the License, or any later version.
  128. %
  129. % This programme is distributed in the hope that it will be useful,
  130. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  131. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  132. % GNU General Public License for more details.
  133. %
  134. % ACKNOWLEDGEMENTS: Thanks to Diego De Rosa for a suggestion/fix how to
  135. % read the value when the key is found but empty.
  136. %--------------------------------------------------------------------------
  137. %----------------
  138. % INIFILE history
  139. %----------------
  140. %
  141. % [v.1.4.2] 12.01.2007
  142. % - FIX: When in read mode and a certain key is found but the value is
  143. % empty, the default value will be used instead.
  144. %
  145. % [v.1.4.1] 12.01.2006
  146. % - FIX: Some minor refinements (speed,...)
  147. %
  148. % [v.1.4.0] 05.12.2006
  149. % - NEW: New 'readall' option added which reads all the sections,
  150. % subsections and keys out
  151. %
  152. % [v.1.3.2 - v.1.3.5] 25.08.2004
  153. % - NEW: Speed improvement for large files - using fread and fwrite instead
  154. % of fscanf and fprintf, respectively
  155. % - NEW: Some minor changes
  156. % - NEW: Writing speed-up
  157. % - NEW: New-line chars are properly set for pc, unix, and mac
  158. %
  159. % [v.1.3.1] 04.05.2004
  160. % - NEW: Comment lines are detected and thus ignored; comment lines are
  161. % lines with first non-empty character being ;
  162. % - NEW: Lines not belonging to any of the recognized types (key, section,
  163. % comment,...) raise an error.
  164. %
  165. % [v.1.3.0] 21.04.2004
  166. % - NEW: 2D Numeric matrices can be read/written
  167. % - FIX: Bug related to read operation and default value has been removed
  168. %
  169. % [v.1.2.0] 30.04.2004
  170. % - NEW: Automatic conversion capability (integers, doubles, and strings)
  171. % added for read and write operations
  172. %
  173. % [v.1.1.0] 04.02.2004
  174. % - FIX: 'writetext' option removed (there was a bug previously)
  175. %
  176. % [v.1.01b] 19.12.2003
  177. % - NEW: A new concept - multiple keys can now be read, written, or deleted
  178. % ALL AT ONCE which makes this function much faster. For example, to
  179. % write 1000 keys, using previous versions it took 157 seconds on a
  180. % 1.5 GHz machine, with this new version it took only 0.9 seconds.
  181. % In general, the speed improvement is greater when a larger number of
  182. % read/written keys is considered (with respect to the older version).
  183. % - NEW: The format of the input parameters has changed. See above.
  184. %
  185. % [v.0.97] 19.11.2003
  186. % - NEW: Additional m-function, strtrim, is no longer needed
  187. %
  188. % [v.0.96] 16.10.2003
  189. % - FIX: Detects empty keys
  190. %
  191. % [v.0.95] 04.07.2003
  192. % - NEW: 'deletekey' option/operation added
  193. % - FIX: A major file refinement to obtain a more compact utility ->
  194. % additional operations can "easily" be implemented
  195. %
  196. % [v.0.91-0.94]
  197. % - FIX: Some minor refinements
  198. %
  199. % [v.0.90] 29.01.2003
  200. % - NEW: First release of this tool
  201. %
  202. %----------------
  203. global NL_CHAR;
  204. % Checks the input arguments
  205. if nargin == 0
  206. disp('INIFILE v1.4.1');
  207. disp('Copyright (c) 2003-2005 by Primoz Cermelj');
  208. disp('This is FREE SOFTWARE');
  209. disp('Type <help inifile> to get more help on its usage');
  210. return
  211. elseif nargin < 2
  212. error('Not enough input arguments');
  213. end
  214. fileName = varargin{1};
  215. operation = varargin{2};
  216. if (strcmpi(operation,'read')) | (strcmpi(operation,'deletekeys'))
  217. if nargin < 3
  218. error('Not enough input arguments.');
  219. end
  220. if ~exist(fileName)
  221. error(['File ' fileName ' does not exist.']);
  222. end
  223. keys = varargin{3};
  224. [m,n] = size(keys);
  225. if n < 3
  226. error('Keys argument must have at least 3 columns for read operation');
  227. end
  228. for ii=1:m
  229. if isempty(keys(ii,3)) | ~ischar(keys{ii,3})
  230. error('Empty or non-char keys are not allowed.');
  231. end
  232. end
  233. elseif (strcmpi(operation,'write'))
  234. if nargin < 3
  235. error('Not enough input arguments');
  236. end
  237. keys = varargin{3};
  238. if nargin < 4 || isempty(varargin{4})
  239. style = 'plain';
  240. else
  241. style = varargin{4};
  242. if ~(strcmpi(style,'plain') | strcmpi(style,'tabbed')) | ~ischar(style)
  243. error('Unsupported style given or style not given as a string');
  244. end
  245. end
  246. [m,n] = size(keys);
  247. if n < 4
  248. error('Keys argument requires 4 columns for write operation');
  249. end
  250. for ii=1:m
  251. if isempty(keys(ii,3)) | ~ischar(keys{ii,3})
  252. error('Empty or non-char keys are not allowed.');
  253. end
  254. end
  255. elseif (strcmpi(operation,'readall'))
  256. %
  257. elseif (~strcmpi(operation,'new'))
  258. error(['Unknown inifile operation: ''' operation '''']);
  259. end
  260. if nargin >= 3
  261. for ii=1:m
  262. for jj=1:3
  263. if ~ischar(keys{ii,jj})
  264. error('All cells from the first 3 columns must be given as strings, even the empty ones.');
  265. end
  266. end
  267. end
  268. end
  269. % Sets the new-line character/string
  270. if ispc
  271. NL_CHAR = '\r\n';
  272. elseif isunix
  273. NL_CHAR = '\n';
  274. else
  275. NL_CHAR = '\r';
  276. end
  277. readsett = [];
  278. result = [];
  279. %----------------------------
  280. % CREATES a new, empty file (rewrites an existing one)
  281. %----------------------------
  282. if strcmpi(operation,'new')
  283. fh = fopen(fileName,'w');
  284. if fh == -1
  285. error(['File: ''' fileName ''' can not be (re)created']);
  286. end
  287. fclose(fh);
  288. return
  289. %----------------------------
  290. % READS the whole data (all keys)
  291. %----------------------------
  292. elseif (strcmpi(operation,'readall'))
  293. [keys,sections,subsections] = readallkeys(fileName);
  294. varargout(1) = {keys};
  295. varargout(2) = {sections};
  296. varargout(3) = {subsections};
  297. return
  298. %----------------------------
  299. % READS key-value pairs out
  300. %----------------------------
  301. elseif (strcmpi(operation,'read'))
  302. result = cell(m,1);
  303. if n >= 4
  304. conversionOp = keys(:,4); % conversion operation: 'i', 'd', or 's' ('') - for each key to be read
  305. else
  306. conversionOp = cellstrings(m,1);
  307. end
  308. if n < 5
  309. defaultValues = cellstrings(m,1);
  310. else
  311. defaultValues = keys(:,5);
  312. end
  313. readsett = defaultValues;
  314. keysIn = keys(:,1:3);
  315. [secsExist,subsecsExist,keysExist,readValues,so,eo] = findkeys(fileName,keysIn);
  316. ind = find(keysExist);
  317. % For those keys that exist but have empty values, replace them with
  318. % the default values
  319. if ~isempty(ind)
  320. ind_empty = zeros(size(ind));
  321. for kk = 1:size(ind,1)
  322. ind_empty(kk) = isempty(readValues{ind(kk)});
  323. end
  324. ind(find(ind_empty)) = [];
  325. readsett(ind) = readValues(ind);
  326. end
  327. % Now, go through all the keys and do the conversion if the conversion
  328. % char is given
  329. for ii=1:m
  330. if ~isempty(conversionOp{ii}) & ~strcmpi(conversionOp{ii},'s')
  331. if strcmpi(conversionOp{ii},'i') | strcmpi(conversionOp{ii},'d')
  332. if ~isnumeric(readsett{ii})
  333. readsett{ii} = str2num(readsett{ii});
  334. end
  335. if strcmpi(conversionOp{ii},'i')
  336. readsett{ii} = round(readsett{ii});
  337. end
  338. if isempty(readsett{ii})
  339. result{ii} = [num2str(ii) '-th key ' keysIn{ii,3} 'or given defaultValue could not be converted using ''' conversionOp{ii} ''' conversion'];
  340. end
  341. else
  342. error(['Invalid conversion char given: ' conversionOp{ii}]);
  343. end
  344. end
  345. end
  346. varargout(1) = {readsett};
  347. varargout(2) = {result};
  348. return
  349. %----------------------------
  350. % WRITES key-value pairs to an existing or non-existing
  351. % file (file can even be empty)
  352. %----------------------------
  353. elseif (strcmpi(operation,'write'))
  354. if m < 1
  355. error('At least one key is needed when writing keys');
  356. end
  357. if ~exist(fileName)
  358. inifile(fileName,'new');
  359. end
  360. for ii=1:m % go through ALL the keys and convert them to strings
  361. keys{ii,4} = n2s(keys{ii,4});
  362. end
  363. writekeys(fileName,keys,style);
  364. return
  365. %----------------------------
  366. % DELETES key-value pairs out
  367. %----------------------------
  368. elseif (strcmpi(operation,'deletekeys'))
  369. deletekeys(fileName,keys);
  370. else
  371. error('Unknown operation for INIFILE.');
  372. end
  373. %--------------------------------------------------
  374. %%%%%%%%%%%%% SUBFUNCTIONS SECTION %%%%%%%%%%%%%%%%
  375. %--------------------------------------------------
  376. %------------------------------------
  377. function [secsExist,subSecsExist,keysExist,values,startOffsets,endOffsets] = findkeys(fileName,keysIn)
  378. % This function parses ini file for keys as given by keysIn. keysIn is a cell
  379. % array of strings having 3 columns; section, subsection and key in each row.
  380. % section and/or subsection can be empty (root section or root subsection)
  381. % but the key can not be empty. The startOffsets and endOffsets are start and
  382. % end bytes that each key occuppies, respectively. If any of the keys doesn't exist,
  383. % startOffset and endOffset for this key are the same. A special case is
  384. % when the key that doesn't exist also corresponds to a non-existing
  385. % section and non-existing subsection. In such a case, the startOffset and
  386. % endOffset have values of -1.
  387. nKeys = size(keysIn,1); % number of keys
  388. nKeysLocated = 0; % number of keys located
  389. secsExist = zeros(nKeys,1); % if section exists (and is non-empty)
  390. subSecsExist = zeros(nKeys,1); % if subsection...
  391. keysExist = zeros(nKeys,1); % if key that we are looking for exists
  392. keysLocated = keysExist; % if the key's position (existing or non-existing) is LOCATED
  393. values = cellstrings(nKeys,1); % read values of keys (strings)
  394. startOffsets = -ones(nKeys,1); % start byte-position of the keys
  395. endOffsets = -ones(nKeys,1); % end byte-position of the keys
  396. keyInd = find(strcmpi(keysIn(:,1),'')); % key indices having [] section (root section)
  397. line = [];
  398. lineN = 0; % line number
  399. currSection = '';
  400. currSubSection = '';
  401. fh = fopen(fileName,'r');
  402. if fh == -1
  403. error(['File: ''' fileName ''' does not exist or can not be opened.']);
  404. end
  405. try
  406. %--- Searching for the keys - their values and start and end locations in bytes
  407. while 1
  408. pos1 = ftell(fh);
  409. line = fgetl(fh);
  410. if line == -1 % end of file, exit
  411. line = [];
  412. break
  413. end
  414. lineN = lineN + 1;
  415. [status,readValue,readKey] = processiniline(line);
  416. if (status == 1) % (new) section found
  417. % Keys that were found as belonging to any previous section
  418. % are now assumed as located (because another
  419. % section is found here which could even be a repeated one)
  420. keyInd = find( ~keysLocated & strcmpi(keysIn(:,1),currSection) );
  421. if length(keyInd)
  422. keysLocated(keyInd) = 1;
  423. nKeysLocated = nKeysLocated + length(keyInd);
  424. end
  425. currSection = readValue;
  426. currSubSection = '';
  427. % Indices to non-located keys belonging to current section
  428. keyInd = find( ~keysLocated & strcmpi(keysIn(:,1),currSection) );
  429. if ~isempty(keyInd)
  430. secsExist(keyInd) = 1;
  431. end
  432. pos2 = ftell(fh);
  433. startOffsets(keyInd) = pos2+1;
  434. endOffsets(keyInd) = pos2+1;
  435. elseif (status == 2) % (new) subsection found
  436. % Keys that were found as belonging to any PREVIOUS section
  437. % and/or subsection are now assumed as located (because another
  438. % subsection is found here which could even be a repeated one)
  439. keyInd = find( ~keysLocated & strcmpi(keysIn(:,1),currSection) & ~keysLocated & strcmpi(keysIn(:,2),currSubSection));
  440. if length(keyInd)
  441. keysLocated(keyInd) = 1;
  442. nKeysLocated = nKeysLocated + length(keyInd);
  443. end
  444. currSubSection = readValue;
  445. % Indices to non-located keys belonging to current section and subsection at the same time
  446. keyInd = find( ~keysLocated & strcmpi(keysIn(:,1),currSection) & ~keysLocated & strcmpi(keysIn(:,2),currSubSection));
  447. if ~isempty(keyInd)
  448. subSecsExist(keyInd) = 1;
  449. end
  450. pos2 = ftell(fh);
  451. startOffsets(keyInd) = pos2+1;
  452. endOffsets(keyInd) = pos2+1;
  453. elseif (status == 3) % key found
  454. if isempty(keyInd)
  455. continue % no keys from 'keys' - from section-subsection par currently in
  456. end
  457. currKey = readValue;
  458. pos2 = ftell(fh); % the last-byte position of the read key - the total sum of chars read so far
  459. for ii=1:length(keyInd)
  460. if strcmpi( keysIn(keyInd(ii),3),readKey ) & ~keysLocated(keyInd(ii))
  461. keysExist(keyInd(ii)) = 1;
  462. startOffsets(keyInd(ii)) = pos1+1;
  463. endOffsets(keyInd(ii)) = pos2;
  464. values{keyInd(ii)} = currKey;
  465. keysLocated(keyInd(ii)) = 1;
  466. nKeysLocated = nKeysLocated + 1;
  467. else
  468. if ~keysLocated(keyInd(ii))
  469. startOffsets(keyInd(ii)) = pos2+1;
  470. endOffsets(keyInd(ii)) = pos2+1;
  471. end
  472. end
  473. end
  474. if nKeysLocated >= nKeys % if all the keys are located stop the searching
  475. break
  476. end
  477. else % general text found (even empty line(s))
  478. if (status == -1)
  479. error(['unknown string found at line ' num2str(lineN)]);
  480. end
  481. end
  482. %--- End of searching
  483. end
  484. fclose(fh);
  485. catch
  486. fclose(fh);
  487. error(['Error parsing the file for keys: ' fileName ': ' lasterr]);
  488. end
  489. %------------------------------------
  490. %------------------------------------
  491. function writekeys(fileName,keys,style)
  492. % Writes keys to the section and subsection pair
  493. % If any of the keys doesn't exist, a new key is added to
  494. % the end of the section-subsection pair otherwise the key is updated (changed).
  495. % Keys is a 4-column cell array of strings.
  496. global NL_CHAR;
  497. RETURN = sprintf('\r');
  498. NEWLINE = sprintf('\n');
  499. [m,n] = size(keys);
  500. if n < 4
  501. error('Keys to be written are given in an invalid format.');
  502. end
  503. % Get keys position first using findkeys
  504. keysIn = keys;
  505. [secsExist,subSecsExist,keysExist,readValues,so,eo] = findkeys(fileName,keys(:,1:3));
  506. % Read the whole file's contents out
  507. fh = fopen(fileName,'r');
  508. if fh == -1
  509. error(['File: ''' fileName ''' does not exist or can not be opened.']);
  510. end
  511. try
  512. dataout = fread(fh,'char=>char')';
  513. catch
  514. fclose(fh);
  515. error(lasterr);
  516. end
  517. fclose(fh);
  518. %--- Rewriting the file -> writing the refined contents
  519. fh = fopen(fileName,'w');
  520. if fh == -1
  521. error(['File: ''' fileName ''' does not exist or can not be opened.']);
  522. end
  523. try
  524. tab1 = [];
  525. if strcmpi(style,'tabbed')
  526. tab1 = sprintf('\t');
  527. end
  528. % Proper sorting of keys is cruical at this point in order to avoid
  529. % inproper key-writing.
  530. % Find keys with -1 offsets - keys with non-existing section AND
  531. % subsection - keys that will be added to the end of the file
  532. fs = length(dataout); % file size in bytes
  533. nAddedKeys = 0;
  534. ind = find(so==-1);
  535. if ~isempty(ind)
  536. so(ind) = (fs+10); % make sure these keys will come to the end when sorting
  537. eo(ind) = (fs+10);
  538. nAddedKeys = length(ind);
  539. end
  540. % Sort keys according to start- and end-offsets
  541. [dummy,ind] = sort(so,1);
  542. so = so(ind);
  543. eo = eo(ind);
  544. keysIn = keysIn(ind,:);
  545. keysExist = keysExist(ind);
  546. secsExist = secsExist(ind);
  547. subSecsExist = subSecsExist(ind);
  548. readValues = readValues(ind);
  549. values = keysIn(:,4);
  550. % Find keys with equal start offset (so) and additionally sort them
  551. % (locally). These are non-existing keys, including the ones whose
  552. % section and subsection will also be added.
  553. nKeys = size(so,1);
  554. fullInd = 1:nKeys;
  555. ii = 1;
  556. while ii < nKeys
  557. ind = find(so==so(ii));
  558. if ~isempty(ind) && length(ind) > 1
  559. n = length(ind);
  560. from = ind(1);
  561. to = ind(end);
  562. tmpKeys = keysIn( ind,: );
  563. [tmpKeys,ind2] = sortrows( lower(tmpKeys) );
  564. fullInd(from:to) = ind(ind2);
  565. ii = ii + n;
  566. else
  567. ii = ii + 1;
  568. end
  569. end
  570. % Final (re)sorting
  571. so = so(fullInd);
  572. eo = eo(fullInd);
  573. keysIn = keysIn(fullInd,:);
  574. keysExist = keysExist(fullInd);
  575. secsExist = secsExist(fullInd);
  576. subSecsExist = subSecsExist(fullInd);
  577. readValues = readValues(fullInd);
  578. values = keysIn(:,4);
  579. % Refined data - datain
  580. datain = [];
  581. for ii=1:nKeys % go through all the keys, existing and non-existing ones
  582. if ii==1
  583. from = 1; % from byte-offset of original data (dataout)
  584. else
  585. from = eo(ii-1);
  586. if keysExist(ii-1)
  587. from = from + 1;
  588. end
  589. end
  590. to = min(so(ii)-1,fs); % to byte-offset of original data (dataout)
  591. if ~isempty(dataout)
  592. datain = [datain dataout(from:to)]; % the lines before the key
  593. end
  594. if length(datain) & (~(datain(end)==RETURN | datain(end)==NEWLINE))
  595. datain = [datain, sprintf(NL_CHAR)];
  596. end
  597. tab = [];
  598. if ~keysExist(ii)
  599. if ~secsExist(ii) && ~isempty(keysIn(ii,1))
  600. if ~isempty(keysIn{ii,1})
  601. datain = [datain sprintf(['%s' NL_CHAR],['[' keysIn{ii,1} ']'])];
  602. end
  603. % Key-indices with the same section as this, ii-th key (even empty sections are considered)
  604. ind = find( strcmpi( keysIn(:,1), keysIn(ii,1)) );
  605. % This section exists at all keys corresponding to the same section from know on (even the empty ones)
  606. secsExist(ind) = 1;
  607. end
  608. if ~subSecsExist(ii) && ~isempty(keysIn(ii,2))
  609. if ~isempty( keysIn{ii,2})
  610. if secsExist(ii); tab = tab1; end;
  611. datain = [datain sprintf(['%s' NL_CHAR],[tab '{' keysIn{ii,2} '}'])];
  612. end
  613. % Key-indices with the same section AND subsection as this, ii-th key
  614. % (even empty sections and subsections are considered)
  615. ind = find( strcmpi( keysIn(:,1), keysIn(ii,1)) & strcmpi( keysIn(:,2), keysIn(ii,2)) );
  616. % This subsection exists at all keys corresponding to the
  617. % same section and subsection from know on (even the empty ones)
  618. subSecsExist(ind) = 1;
  619. end
  620. end
  621. if secsExist(ii) & (~isempty(keysIn{ii,1})); tab = tab1; end;
  622. if subSecsExist(ii) & (~isempty(keysIn{ii,2})); tab = [tab tab1]; end;
  623. datain = [datain sprintf(['%s' NL_CHAR],[tab keysIn{ii,3} ' = ' values{ii}])];
  624. end
  625. from = eo(ii);
  626. if keysExist(ii)
  627. from = from + 1;
  628. end
  629. to = length(dataout);
  630. if from < to
  631. datain = [datain dataout(from:to)];
  632. end
  633. fwrite(fh,datain,'char');
  634. catch
  635. fclose(fh);
  636. error(['Error writing keys to file: ''' fileName ''' : ' lasterr]);
  637. end
  638. fclose(fh);
  639. %------------------------------------
  640. %------------------------------------
  641. function deletekeys(fileName,keys)
  642. % Deletes keys and their values out; keys must have at least 3 columns:
  643. % section, subsection, and the key
  644. [m,n] = size(keys);
  645. if n < 3
  646. error('Keys to be deleted are given in an invalid format.');
  647. end
  648. % Get keys position first
  649. keysIn = keys;
  650. [secsExist,subSecsExist,keysExist,readValues,so,eo] = findkeys(fileName,keys(:,1:3));
  651. % Read the whole file's contents out
  652. fh = fopen(fileName,'r');
  653. if fh == -1
  654. error(['File: ''' fileName ''' does not exist or can not be opened.']);
  655. end
  656. try
  657. dataout = fread(fh,'char=>char')';
  658. catch
  659. fclose(fh);
  660. rethrow(lasterror);
  661. end
  662. fclose(fh);
  663. %--- Rewriting the file -> writing the refined content
  664. fh = fopen(fileName,'w');
  665. if fh == -1
  666. error(['File: ''' fileName ''' does not exist or can not be opened.']);
  667. end
  668. try
  669. ind = find(keysExist);
  670. nExistingKeys = length(ind);
  671. datain = dataout;
  672. if nExistingKeys
  673. % Filtering - retain only the existing keys...
  674. fs = length(dataout); % file size in bytes
  675. so = so(ind);
  676. eo = eo(ind);
  677. keysIn = keysIn(ind,:);
  678. % ...and sorting
  679. [so,ind] = sort(so);
  680. eo = eo(ind);
  681. keysIn = keysIn(ind,:);
  682. % Refined data - datain
  683. datain = [];
  684. for ii=1:nExistingKeys % go through all the existing keys
  685. if ii==1
  686. from = 1; % from byte-offset of original data (dataout)
  687. else
  688. from = eo(ii-1)+1;
  689. end
  690. to = so(ii)-1; % to byte-offset of original data (dataout)
  691. if ~isempty(dataout)
  692. datain = [datain dataout(from:to)]; % the lines before the key
  693. end
  694. end
  695. from = eo(ii)+1;
  696. to = length(dataout);
  697. if from < to
  698. datain = [datain dataout(from:to)];
  699. end
  700. end
  701. fwrite(fh,datain,'char');
  702. catch
  703. fclose(fh);
  704. error(['Error deleting keys from file: ''' fileName ''' : ' lasterr]);
  705. end
  706. fclose(fh);
  707. %------------------------------------
  708. %------------------------------------
  709. function [keys,sections,subsections] = readallkeys(fileName)
  710. % Reads all the keys out as well as the sections and subsections
  711. keys = [];
  712. sections = [];
  713. subsections = [];
  714. % Read the whole file's contents out
  715. try
  716. dataout = textread(fileName,'%s','delimiter','\n');
  717. catch
  718. error(['File: ''' fileName ''' does not exist or can not be opened.']);
  719. end
  720. nLines = size(dataout,1);
  721. % Go through all the lines and construct the keys variable
  722. keys = cell(nLines,4);
  723. sections = cell(nLines,1);
  724. subsections = cell(nLines,2);
  725. keyN = 0;
  726. secN = 0;
  727. subsecN = 0;
  728. secStr = '';
  729. subsecStr = '';
  730. for ii=1:nLines
  731. [status,value,key] = processiniline(dataout{ii});
  732. if status == 1
  733. secN = secN + 1;
  734. secStr = value;
  735. sections(secN) = {secStr};
  736. elseif status == 2
  737. subsecN = subsecN + 1;
  738. subsecStr = value;
  739. subsections(subsecN,:) = {secStr,subsecStr};
  740. elseif status == 3
  741. keyN = keyN + 1;
  742. keys(keyN,:) = {secStr,subsecStr,key,value};
  743. end
  744. end
  745. keys(keyN+1:end,:) = [];
  746. sections(secN+1:end,:) = [];
  747. subsections(subsecN+1:end,:) = [];
  748. %------------------------------------
  749. %------------------------------------
  750. function [status,value,key] = processiniline(line)
  751. % Processes a line read from the ini file and
  752. % returns the following values:
  753. % - status: -1 => unknown string found
  754. % 0 => empty line found
  755. % 1 => section found
  756. % 2 => subsection found
  757. % 3 => key-value pair found
  758. % 4 => comment line found (starting with ;)
  759. % - value: value-string of a key, section, subsection, comment, or unknown string
  760. % - key: key as string
  761. status = 0;
  762. value = [];
  763. key = [];
  764. line = strim(line); % removes any leading and trailing spaces
  765. if isempty(line) % empty line
  766. return
  767. end
  768. if strcmpi(line(1),';') % comment found
  769. status = 4;
  770. value = line(2:end);
  771. elseif (line(1) == '[') & (line(end) == ']') & (length(line) >= 3) % section found
  772. value = lower(line(2:end-1));
  773. status = 1;
  774. elseif (line(1) == '{') &... % subsection found
  775. (line(end) == '}') & (length(line) >= 3)
  776. value = lower(line(2:end-1));
  777. status = 2;
  778. else % either key-value pair or unknown string
  779. pos = findstr(line,'=');
  780. if ~isempty(pos) % key-value pair found
  781. status = 3;
  782. key = lower(line(1:pos-1));
  783. value = line(pos+1:end);
  784. key = strim(key); % removes any leading and trailing spaces
  785. value = strim(value); % removes any leading and trailing spaces
  786. if isempty(key) % empty keys are not allowed
  787. status = 0;
  788. key = [];
  789. value = [];
  790. end
  791. else % unknown string found
  792. status = -1;
  793. value = line;
  794. end
  795. end
  796. %------------------------------------
  797. function outstr = strim(str)
  798. % Removes leading and trailing spaces (spaces, tabs, endlines,...)
  799. % from the str string.
  800. if isnumeric(str);
  801. outstr = str;
  802. return
  803. end
  804. ind = find( ~isspace(str) ); % indices of the non-space characters in the str
  805. if isempty(ind)
  806. outstr = [];
  807. else
  808. outstr = str( ind(1):ind(end) );
  809. end
  810. %------------------------------------
  811. function cs = cellstrings(m,n)
  812. % Creates a m x n cell array of empty strings - ''
  813. cs = cell(m,n);
  814. cs(:) = {''};
  815. %------------------------------------
  816. function y = n2s(x)
  817. % Converts numeric matrix to string representation.
  818. % Example: x given as [1 2;3 4] returns y = '1,2;3;4'
  819. if ischar(x) | isempty(x)
  820. y = x;
  821. return
  822. end
  823. [m,n] = size(x);
  824. y = [num2str(x(1,:),'%15.6g')];
  825. for ii=2:m
  826. y = [y ';' num2str(x(ii,:),'%15.6g')];
  827. end