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 | % First release on 29.01.2003 |
---|
113 | % (c) Primoz Cermelj, Slovenia |
---|
114 | % Contact: primoz.cermelj@gmail.com |
---|
115 | % Download location: http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=2976&objectType=file |
---|
116 | % |
---|
117 | % Version: 1.4.2 |
---|
118 | % Last revision: 12.01.2007 |
---|
119 | % |
---|
120 | % Bug reports, questions, etc. can be sent to the e-mail given above. |
---|
121 | % |
---|
122 | % ACKNOWLEDGEMENTS: Thanks to Diego De Rosa for a suggestion/fix how to |
---|
123 | % read the value when the key is found but empty. |
---|
124 | %-------------------------------------------------------------------------- |
---|
125 | |
---|
126 | %---------------- |
---|
127 | % INIFILE history |
---|
128 | %---------------- |
---|
129 | % |
---|
130 | % [v.1.4.2] 12.01.2007 |
---|
131 | % - FIX: When in read mode and a certain key is found but the value is |
---|
132 | % empty, the default value will be used instead. |
---|
133 | % |
---|
134 | % [v.1.4.1] 12.01.2006 |
---|
135 | % - FIX: Some minor refinements (speed,...) |
---|
136 | % |
---|
137 | % [v.1.4.0] 05.12.2006 |
---|
138 | % - NEW: New 'readall' option added which reads all the sections, |
---|
139 | % subsections and keys out |
---|
140 | % |
---|
141 | % [v.1.3.2 - v.1.3.5] 25.08.2004 |
---|
142 | % - NEW: Speed improvement for large files - using fread and fwrite instead |
---|
143 | % of fscanf and fprintf, respectively |
---|
144 | % - NEW: Some minor changes |
---|
145 | % - NEW: Writing speed-up |
---|
146 | % - NEW: New-line chars are properly set for pc, unix, and mac |
---|
147 | % |
---|
148 | % [v.1.3.1] 04.05.2004 |
---|
149 | % - NEW: Comment lines are detected and thus ignored; comment lines are |
---|
150 | % lines with first non-empty character being ; |
---|
151 | % - NEW: Lines not belonging to any of the recognized types (key, section, |
---|
152 | % comment,...) raise an error. |
---|
153 | % |
---|
154 | % [v.1.3.0] 21.04.2004 |
---|
155 | % - NEW: 2D Numeric matrices can be read/written |
---|
156 | % - FIX: Bug related to read operation and default value has been removed |
---|
157 | % |
---|
158 | % [v.1.2.0] 30.04.2004 |
---|
159 | % - NEW: Automatic conversion capability (integers, doubles, and strings) |
---|
160 | % added for read and write operations |
---|
161 | % |
---|
162 | % [v.1.1.0] 04.02.2004 |
---|
163 | % - FIX: 'writetext' option removed (there was a bug previously) |
---|
164 | % |
---|
165 | % [v.1.01b] 19.12.2003 |
---|
166 | % - NEW: A new concept - multiple keys can now be read, written, or deleted |
---|
167 | % ALL AT ONCE which makes this function much faster. For example, to |
---|
168 | % write 1000 keys, using previous versions it took 157 seconds on a |
---|
169 | % 1.5 GHz machine, with this new version it took only 0.9 seconds. |
---|
170 | % In general, the speed improvement is greater when a larger number of |
---|
171 | % read/written keys is considered (with respect to the older version). |
---|
172 | % - NEW: The format of the input parameters has changed. See above. |
---|
173 | % |
---|
174 | % [v.0.97] 19.11.2003 |
---|
175 | % - NEW: Additional m-function, strtrim, is no longer needed |
---|
176 | % |
---|
177 | % [v.0.96] 16.10.2003 |
---|
178 | % - FIX: Detects empty keys |
---|
179 | % |
---|
180 | % [v.0.95] 04.07.2003 |
---|
181 | % - NEW: 'deletekey' option/operation added |
---|
182 | % - FIX: A major file refinement to obtain a more compact utility -> |
---|
183 | % additional operations can "easily" be implemented |
---|
184 | % |
---|
185 | % [v.0.91-0.94] |
---|
186 | % - FIX: Some minor refinements |
---|
187 | % |
---|
188 | % [v.0.90] 29.01.2003 |
---|
189 | % - NEW: First release of this tool |
---|
190 | % |
---|
191 | %---------------- |
---|
192 | |
---|
193 | global NL_CHAR; |
---|
194 | |
---|
195 | % Checks the input arguments |
---|
196 | if nargin == 0 |
---|
197 | disp('INIFILE v1.4.2'); |
---|
198 | disp('Copyright (c) 2003-2007 Primoz Cermelj'); |
---|
199 | disp('This is FREE SOFTWARE'); |
---|
200 | disp('Type <help inifile> to get more help on its usage'); |
---|
201 | return |
---|
202 | elseif nargin < 2 |
---|
203 | error('Not enough input arguments'); |
---|
204 | end |
---|
205 | |
---|
206 | fileName = varargin{1}; |
---|
207 | operation = varargin{2}; |
---|
208 | |
---|
209 | if (strcmpi(operation,'read')) | (strcmpi(operation,'deletekeys')) |
---|
210 | if nargin < 3 |
---|
211 | error('Not enough input arguments.'); |
---|
212 | end |
---|
213 | if ~exist(fileName) |
---|
214 | error(['File ' fileName ' does not exist.']); |
---|
215 | end |
---|
216 | keys = varargin{3}; |
---|
217 | [m,n] = size(keys); |
---|
218 | if n < 3 |
---|
219 | error('Keys argument must have at least 3 columns for read operation'); |
---|
220 | end |
---|
221 | for ii=1:m |
---|
222 | if isempty(keys(ii,3)) | ~ischar(keys{ii,3}) |
---|
223 | error('Empty or non-char keys are not allowed.'); |
---|
224 | end |
---|
225 | end |
---|
226 | elseif (strcmpi(operation,'write')) |
---|
227 | if nargin < 3 |
---|
228 | error('Not enough input arguments'); |
---|
229 | end |
---|
230 | keys = varargin{3}; |
---|
231 | if nargin < 4 || isempty(varargin{4}) |
---|
232 | style = 'plain'; |
---|
233 | else |
---|
234 | style = varargin{4}; |
---|
235 | if ~(strcmpi(style,'plain') | strcmpi(style,'tabbed')) | ~ischar(style) |
---|
236 | error('Unsupported style given or style not given as a string'); |
---|
237 | end |
---|
238 | end |
---|
239 | [m,n] = size(keys); |
---|
240 | if n < 4 |
---|
241 | error('Keys argument requires 4 columns for write operation'); |
---|
242 | end |
---|
243 | for ii=1:m |
---|
244 | if isempty(keys(ii,3)) | ~ischar(keys{ii,3}) |
---|
245 | error('Empty or non-char keys are not allowed.'); |
---|
246 | end |
---|
247 | end |
---|
248 | elseif (strcmpi(operation,'readall')) |
---|
249 | % |
---|
250 | elseif (~strcmpi(operation,'new')) |
---|
251 | error(['Unknown inifile operation: ''' operation '''']); |
---|
252 | end |
---|
253 | if nargin >= 3 |
---|
254 | for ii=1:m |
---|
255 | for jj=1:3 |
---|
256 | if ~ischar(keys{ii,jj}) |
---|
257 | error('All cells from the first 3 columns must be given as strings, even the empty ones.'); |
---|
258 | end |
---|
259 | end |
---|
260 | end |
---|
261 | end |
---|
262 | |
---|
263 | |
---|
264 | % Sets the new-line character/string |
---|
265 | if ispc |
---|
266 | NL_CHAR = '\r\n'; |
---|
267 | elseif isunix |
---|
268 | NL_CHAR = '\n'; |
---|
269 | else |
---|
270 | NL_CHAR = '\r'; |
---|
271 | end |
---|
272 | |
---|
273 | readsett = []; |
---|
274 | result = []; |
---|
275 | |
---|
276 | %---------------------------- |
---|
277 | % CREATES a new, empty file (rewrites an existing one) |
---|
278 | %---------------------------- |
---|
279 | if strcmpi(operation,'new') |
---|
280 | fh = fopen(fileName,'w'); |
---|
281 | if fh == -1 |
---|
282 | error(['File: ''' fileName ''' can not be (re)created']); |
---|
283 | end |
---|
284 | fclose(fh); |
---|
285 | return |
---|
286 | |
---|
287 | %---------------------------- |
---|
288 | % READS the whole data (all keys) |
---|
289 | %---------------------------- |
---|
290 | elseif (strcmpi(operation,'readall')) |
---|
291 | [keys,sections,subsections] = readallkeys(fileName); |
---|
292 | varargout(1) = {keys}; |
---|
293 | varargout(2) = {sections}; |
---|
294 | varargout(3) = {subsections}; |
---|
295 | return |
---|
296 | |
---|
297 | %---------------------------- |
---|
298 | % READS key-value pairs out |
---|
299 | %---------------------------- |
---|
300 | elseif (strcmpi(operation,'read')) |
---|
301 | result = cell(m,1); |
---|
302 | if n >= 4 |
---|
303 | conversionOp = keys(:,4); % conversion operation: 'i', 'd', or 's' ('') - for each key to be read |
---|
304 | else |
---|
305 | conversionOp = cellstrings(m,1); |
---|
306 | end |
---|
307 | if n < 5 |
---|
308 | defaultValues = cellstrings(m,1); |
---|
309 | else |
---|
310 | defaultValues = keys(:,5); |
---|
311 | end |
---|
312 | readsett = defaultValues; |
---|
313 | keysIn = keys(:,1:3); |
---|
314 | [secsExist,subsecsExist,keysExist,readValues,so,eo] = findkeys(fileName,keysIn); |
---|
315 | ind = find(keysExist); |
---|
316 | % For those keys that exist but have empty values, replace them with |
---|
317 | % the default values |
---|
318 | if ~isempty(ind) |
---|
319 | ind_empty = zeros(size(ind)); |
---|
320 | for kk = 1:size(ind,1) |
---|
321 | ind_empty(kk) = isempty(readValues{ind(kk)}); |
---|
322 | end |
---|
323 | ind(find(ind_empty)) = []; |
---|
324 | readsett(ind) = readValues(ind); |
---|
325 | end |
---|
326 | % Now, go through all the keys and do the conversion if the conversion |
---|
327 | % char is given |
---|
328 | for ii=1:m |
---|
329 | if ~isempty(conversionOp{ii}) & ~strcmpi(conversionOp{ii},'s') |
---|
330 | if strcmpi(conversionOp{ii},'i') | strcmpi(conversionOp{ii},'d') |
---|
331 | if ~isnumeric(readsett{ii}) |
---|
332 | readsett{ii} = str2num(readsett{ii}); |
---|
333 | end |
---|
334 | if strcmpi(conversionOp{ii},'i') |
---|
335 | readsett{ii} = round(readsett{ii}); |
---|
336 | end |
---|
337 | if isempty(readsett{ii}) |
---|
338 | result{ii} = [num2str(ii) '-th key ' keysIn{ii,3} 'or given defaultValue could not be converted using ''' conversionOp{ii} ''' conversion']; |
---|
339 | end |
---|
340 | else |
---|
341 | error(['Invalid conversion char given: ' conversionOp{ii}]); |
---|
342 | end |
---|
343 | end |
---|
344 | end |
---|
345 | varargout(1) = {readsett}; |
---|
346 | varargout(2) = {result}; |
---|
347 | return |
---|
348 | |
---|
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 | %---------------------------- |
---|
367 | % DELETES key-value pairs out |
---|
368 | %---------------------------- |
---|
369 | elseif (strcmpi(operation,'deletekeys')) |
---|
370 | deletekeys(fileName,keys); |
---|
371 | |
---|
372 | |
---|
373 | |
---|
374 | else |
---|
375 | error('Unknown operation for INIFILE.'); |
---|
376 | end |
---|
377 | |
---|
378 | |
---|
379 | |
---|
380 | |
---|
381 | %-------------------------------------------------- |
---|
382 | %%%%%%%%%%%%% SUBFUNCTIONS SECTION %%%%%%%%%%%%%%%% |
---|
383 | %-------------------------------------------------- |
---|
384 | |
---|
385 | |
---|
386 | %------------------------------------ |
---|
387 | function [secsExist,subSecsExist,keysExist,values,startOffsets,endOffsets] = findkeys(fileName,keysIn) |
---|
388 | % This function parses ini file for keys as given by keysIn. keysIn is a cell |
---|
389 | % array of strings having 3 columns; section, subsection and key in each row. |
---|
390 | % section and/or subsection can be empty (root section or root subsection) |
---|
391 | % but the key can not be empty. The startOffsets and endOffsets are start and |
---|
392 | % end bytes that each key occuppies, respectively. If any of the keys doesn't exist, |
---|
393 | % startOffset and endOffset for this key are the same. A special case is |
---|
394 | % when the key that doesn't exist also corresponds to a non-existing |
---|
395 | % section and non-existing subsection. In such a case, the startOffset and |
---|
396 | % endOffset have values of -1. |
---|
397 | |
---|
398 | nKeys = size(keysIn,1); % number of keys |
---|
399 | nKeysLocated = 0; % number of keys located |
---|
400 | secsExist = zeros(nKeys,1); % if section exists (and is non-empty) |
---|
401 | subSecsExist = zeros(nKeys,1); % if subsection... |
---|
402 | keysExist = zeros(nKeys,1); % if key that we are looking for exists |
---|
403 | keysLocated = keysExist; % if the key's position (existing or non-existing) is LOCATED |
---|
404 | values = cellstrings(nKeys,1); % read values of keys (strings) |
---|
405 | startOffsets = -ones(nKeys,1); % start byte-position of the keys |
---|
406 | endOffsets = -ones(nKeys,1); % end byte-position of the keys |
---|
407 | |
---|
408 | keyInd = find(strcmpi(keysIn(:,1),'')); % key indices having [] section (root section) |
---|
409 | |
---|
410 | line = []; |
---|
411 | lineN = 0; % line number |
---|
412 | currSection = ''; |
---|
413 | currSubSection = ''; |
---|
414 | |
---|
415 | fh = fopen(fileName,'r'); |
---|
416 | if fh == -1 |
---|
417 | error(['File: ''' fileName ''' does not exist or can not be opened.']); |
---|
418 | end |
---|
419 | |
---|
420 | try |
---|
421 | %--- Searching for the keys - their values and start and end locations in bytes |
---|
422 | while 1 |
---|
423 | |
---|
424 | pos1 = ftell(fh); |
---|
425 | line = fgetl(fh); |
---|
426 | if line == -1 % end of file, exit |
---|
427 | line = []; |
---|
428 | break |
---|
429 | end |
---|
430 | lineN = lineN + 1; |
---|
431 | [status,readValue,readKey] = processiniline(line); |
---|
432 | if (status == 1) % (new) section found |
---|
433 | % Keys that were found as belonging to any previous section |
---|
434 | % are now assumed as located (because another |
---|
435 | % section is found here which could even be a repeated one) |
---|
436 | keyInd = find( ~keysLocated & strcmpi(keysIn(:,1),currSection) ); |
---|
437 | if length(keyInd) |
---|
438 | keysLocated(keyInd) = 1; |
---|
439 | nKeysLocated = nKeysLocated + length(keyInd); |
---|
440 | end |
---|
441 | currSection = readValue; |
---|
442 | currSubSection = ''; |
---|
443 | % Indices to non-located keys belonging to current section |
---|
444 | keyInd = find( ~keysLocated & strcmpi(keysIn(:,1),currSection) ); |
---|
445 | if ~isempty(keyInd) |
---|
446 | secsExist(keyInd) = 1; |
---|
447 | end |
---|
448 | pos2 = ftell(fh); |
---|
449 | startOffsets(keyInd) = pos2+1; |
---|
450 | endOffsets(keyInd) = pos2+1; |
---|
451 | elseif (status == 2) % (new) subsection found |
---|
452 | % Keys that were found as belonging to any PREVIOUS section |
---|
453 | % and/or subsection are now assumed as located (because another |
---|
454 | % subsection is found here which could even be a repeated one) |
---|
455 | keyInd = find( ~keysLocated & strcmpi(keysIn(:,1),currSection) & ~keysLocated & strcmpi(keysIn(:,2),currSubSection)); |
---|
456 | if length(keyInd) |
---|
457 | keysLocated(keyInd) = 1; |
---|
458 | nKeysLocated = nKeysLocated + length(keyInd); |
---|
459 | end |
---|
460 | currSubSection = readValue; |
---|
461 | % Indices to non-located keys belonging to current section and subsection at the same time |
---|
462 | keyInd = find( ~keysLocated & strcmpi(keysIn(:,1),currSection) & ~keysLocated & strcmpi(keysIn(:,2),currSubSection)); |
---|
463 | if ~isempty(keyInd) |
---|
464 | subSecsExist(keyInd) = 1; |
---|
465 | end |
---|
466 | pos2 = ftell(fh); |
---|
467 | startOffsets(keyInd) = pos2+1; |
---|
468 | endOffsets(keyInd) = pos2+1; |
---|
469 | elseif (status == 3) % key found |
---|
470 | if isempty(keyInd) |
---|
471 | continue % no keys from 'keys' - from section-subsection par currently in |
---|
472 | end |
---|
473 | currKey = readValue; |
---|
474 | pos2 = ftell(fh); % the last-byte position of the read key - the total sum of chars read so far |
---|
475 | for ii=1:length(keyInd) |
---|
476 | if strcmpi( keysIn(keyInd(ii),3),readKey ) & ~keysLocated(keyInd(ii)) |
---|
477 | keysExist(keyInd(ii)) = 1; |
---|
478 | startOffsets(keyInd(ii)) = pos1+1; |
---|
479 | endOffsets(keyInd(ii)) = pos2; |
---|
480 | values{keyInd(ii)} = currKey; |
---|
481 | keysLocated(keyInd(ii)) = 1; |
---|
482 | nKeysLocated = nKeysLocated + 1; |
---|
483 | else |
---|
484 | if ~keysLocated(keyInd(ii)) |
---|
485 | startOffsets(keyInd(ii)) = pos2+1; |
---|
486 | endOffsets(keyInd(ii)) = pos2+1; |
---|
487 | end |
---|
488 | end |
---|
489 | end |
---|
490 | if nKeysLocated >= nKeys % if all the keys are located stop the searching |
---|
491 | break |
---|
492 | end |
---|
493 | else % general text found (even empty line(s)) |
---|
494 | if (status == -1) |
---|
495 | error(['unknown string found at line ' num2str(lineN)]); |
---|
496 | end |
---|
497 | end |
---|
498 | %--- End of searching |
---|
499 | end |
---|
500 | fclose(fh); |
---|
501 | catch |
---|
502 | fclose(fh); |
---|
503 | error(['Error parsing the file for keys: ' fileName ': ' lasterr]); |
---|
504 | end |
---|
505 | %------------------------------------ |
---|
506 | |
---|
507 | |
---|
508 | |
---|
509 | |
---|
510 | %------------------------------------ |
---|
511 | function writekeys(fileName,keys,style) |
---|
512 | % Writes keys to the section and subsection pair |
---|
513 | % If any of the keys doesn't exist, a new key is added to |
---|
514 | % the end of the section-subsection pair otherwise the key is updated (changed). |
---|
515 | % Keys is a 4-column cell array of strings. |
---|
516 | |
---|
517 | global NL_CHAR; |
---|
518 | |
---|
519 | RETURN = sprintf('\r'); |
---|
520 | NEWLINE = sprintf('\n'); |
---|
521 | |
---|
522 | [m,n] = size(keys); |
---|
523 | if n < 4 |
---|
524 | error('Keys to be written are given in an invalid format.'); |
---|
525 | end |
---|
526 | |
---|
527 | % Get keys position first using findkeys |
---|
528 | keysIn = keys; |
---|
529 | [secsExist,subSecsExist,keysExist,readValues,so,eo] = findkeys(fileName,keys(:,1:3)); |
---|
530 | |
---|
531 | % Read the whole file's contents out |
---|
532 | fh = fopen(fileName,'r'); |
---|
533 | if fh == -1 |
---|
534 | error(['File: ''' fileName ''' does not exist or can not be opened.']); |
---|
535 | end |
---|
536 | try |
---|
537 | dataout = fread(fh,'char=>char')'; |
---|
538 | catch |
---|
539 | fclose(fh); |
---|
540 | error(lasterr); |
---|
541 | end |
---|
542 | fclose(fh); |
---|
543 | |
---|
544 | %--- Rewriting the file -> writing the refined contents |
---|
545 | fh = fopen(fileName,'w'); |
---|
546 | if fh == -1 |
---|
547 | error(['File: ''' fileName ''' does not exist or can not be opened.']); |
---|
548 | end |
---|
549 | try |
---|
550 | tab1 = []; |
---|
551 | if strcmpi(style,'tabbed') |
---|
552 | tab1 = sprintf('\t'); |
---|
553 | end |
---|
554 | % Proper sorting of keys is cruical at this point in order to avoid |
---|
555 | % inproper key-writing. |
---|
556 | |
---|
557 | % Find keys with -1 offsets - keys with non-existing section AND |
---|
558 | % subsection - keys that will be added to the end of the file |
---|
559 | fs = length(dataout); % file size in bytes |
---|
560 | nAddedKeys = 0; |
---|
561 | ind = find(so==-1); |
---|
562 | if ~isempty(ind) |
---|
563 | so(ind) = (fs+10); % make sure these keys will come to the end when sorting |
---|
564 | eo(ind) = (fs+10); |
---|
565 | nAddedKeys = length(ind); |
---|
566 | end |
---|
567 | |
---|
568 | % Sort keys according to start- and end-offsets |
---|
569 | [dummy,ind] = sort(so,1); |
---|
570 | so = so(ind); |
---|
571 | eo = eo(ind); |
---|
572 | keysIn = keysIn(ind,:); |
---|
573 | keysExist = keysExist(ind); |
---|
574 | secsExist = secsExist(ind); |
---|
575 | subSecsExist = subSecsExist(ind); |
---|
576 | readValues = readValues(ind); |
---|
577 | values = keysIn(:,4); |
---|
578 | |
---|
579 | % Find keys with equal start offset (so) and additionally sort them |
---|
580 | % (locally). These are non-existing keys, including the ones whose |
---|
581 | % section and subsection will also be added. |
---|
582 | nKeys = size(so,1); |
---|
583 | fullInd = 1:nKeys; |
---|
584 | ii = 1; |
---|
585 | while ii < nKeys |
---|
586 | ind = find(so==so(ii)); |
---|
587 | if ~isempty(ind) && length(ind) > 1 |
---|
588 | n = length(ind); |
---|
589 | from = ind(1); |
---|
590 | to = ind(end); |
---|
591 | tmpKeys = keysIn( ind,: ); |
---|
592 | [tmpKeys,ind2] = sortrows( lower(tmpKeys) ); |
---|
593 | fullInd(from:to) = ind(ind2); |
---|
594 | ii = ii + n; |
---|
595 | else |
---|
596 | ii = ii + 1; |
---|
597 | end |
---|
598 | end |
---|
599 | |
---|
600 | % Final (re)sorting |
---|
601 | so = so(fullInd); |
---|
602 | eo = eo(fullInd); |
---|
603 | keysIn = keysIn(fullInd,:); |
---|
604 | keysExist = keysExist(fullInd); |
---|
605 | secsExist = secsExist(fullInd); |
---|
606 | subSecsExist = subSecsExist(fullInd); |
---|
607 | readValues = readValues(fullInd); |
---|
608 | values = keysIn(:,4); |
---|
609 | |
---|
610 | % Refined data - datain |
---|
611 | datain = []; |
---|
612 | |
---|
613 | for ii=1:nKeys % go through all the keys, existing and non-existing ones |
---|
614 | if ii==1 |
---|
615 | from = 1; % from byte-offset of original data (dataout) |
---|
616 | else |
---|
617 | from = eo(ii-1); |
---|
618 | if keysExist(ii-1) |
---|
619 | from = from + 1; |
---|
620 | end |
---|
621 | end |
---|
622 | to = min(so(ii)-1,fs); % to byte-offset of original data (dataout) |
---|
623 | |
---|
624 | if ~isempty(dataout) |
---|
625 | datain = [datain dataout(from:to)]; % the lines before the key |
---|
626 | end |
---|
627 | |
---|
628 | if length(datain) & (~(datain(end)==RETURN | datain(end)==NEWLINE)) |
---|
629 | datain = [datain, sprintf(NL_CHAR)]; |
---|
630 | end |
---|
631 | |
---|
632 | tab = []; |
---|
633 | if ~keysExist(ii) |
---|
634 | if ~secsExist(ii) && ~isempty(keysIn(ii,1)) |
---|
635 | if ~isempty(keysIn{ii,1}) |
---|
636 | datain = [datain sprintf(['%s' NL_CHAR],['[' keysIn{ii,1} ']'])]; |
---|
637 | end |
---|
638 | % Key-indices with the same section as this, ii-th key (even empty sections are considered) |
---|
639 | ind = find( strcmpi( keysIn(:,1), keysIn(ii,1)) ); |
---|
640 | % This section exists at all keys corresponding to the same section from know on (even the empty ones) |
---|
641 | secsExist(ind) = 1; |
---|
642 | end |
---|
643 | if ~subSecsExist(ii) && ~isempty(keysIn(ii,2)) |
---|
644 | if ~isempty( keysIn{ii,2}) |
---|
645 | if secsExist(ii); tab = tab1; end; |
---|
646 | datain = [datain sprintf(['%s' NL_CHAR],[tab '{' keysIn{ii,2} '}'])]; |
---|
647 | end |
---|
648 | % Key-indices with the same section AND subsection as this, ii-th key |
---|
649 | % (even empty sections and subsections are considered) |
---|
650 | ind = find( strcmpi( keysIn(:,1), keysIn(ii,1)) & strcmpi( keysIn(:,2), keysIn(ii,2)) ); |
---|
651 | % This subsection exists at all keys corresponding to the |
---|
652 | % same section and subsection from know on (even the empty ones) |
---|
653 | subSecsExist(ind) = 1; |
---|
654 | end |
---|
655 | end |
---|
656 | if secsExist(ii) & (~isempty(keysIn{ii,1})); tab = tab1; end; |
---|
657 | if subSecsExist(ii) & (~isempty(keysIn{ii,2})); tab = [tab tab1]; end; |
---|
658 | datain = [datain sprintf(['%s' NL_CHAR],[tab keysIn{ii,3} ' = ' values{ii}])]; |
---|
659 | end |
---|
660 | from = eo(ii); |
---|
661 | if keysExist(ii) |
---|
662 | from = from + 1; |
---|
663 | end |
---|
664 | to = length(dataout); |
---|
665 | if from < to |
---|
666 | datain = [datain dataout(from:to)]; |
---|
667 | end |
---|
668 | fwrite(fh,datain,'char'); |
---|
669 | catch |
---|
670 | fclose(fh); |
---|
671 | error(['Error writing keys to file: ''' fileName ''' : ' lasterr]); |
---|
672 | end |
---|
673 | fclose(fh); |
---|
674 | %------------------------------------ |
---|
675 | |
---|
676 | |
---|
677 | |
---|
678 | %------------------------------------ |
---|
679 | function deletekeys(fileName,keys) |
---|
680 | % Deletes keys and their values out; keys must have at least 3 columns: |
---|
681 | % section, subsection, and the key |
---|
682 | |
---|
683 | [m,n] = size(keys); |
---|
684 | if n < 3 |
---|
685 | error('Keys to be deleted are given in an invalid format.'); |
---|
686 | end |
---|
687 | |
---|
688 | % Get keys position first |
---|
689 | keysIn = keys; |
---|
690 | [secsExist,subSecsExist,keysExist,readValues,so,eo] = findkeys(fileName,keys(:,1:3)); |
---|
691 | |
---|
692 | % Read the whole file's contents out |
---|
693 | fh = fopen(fileName,'r'); |
---|
694 | if fh == -1 |
---|
695 | error(['File: ''' fileName ''' does not exist or can not be opened.']); |
---|
696 | end |
---|
697 | try |
---|
698 | dataout = fread(fh,'char=>char')'; |
---|
699 | catch |
---|
700 | fclose(fh); |
---|
701 | rethrow(lasterror); |
---|
702 | end |
---|
703 | fclose(fh); |
---|
704 | |
---|
705 | %--- Rewriting the file -> writing the refined content |
---|
706 | fh = fopen(fileName,'w'); |
---|
707 | if fh == -1 |
---|
708 | error(['File: ''' fileName ''' does not exist or can not be opened.']); |
---|
709 | end |
---|
710 | try |
---|
711 | ind = find(keysExist); |
---|
712 | nExistingKeys = length(ind); |
---|
713 | datain = dataout; |
---|
714 | |
---|
715 | if nExistingKeys |
---|
716 | % Filtering - retain only the existing keys... |
---|
717 | fs = length(dataout); % file size in bytes |
---|
718 | so = so(ind); |
---|
719 | eo = eo(ind); |
---|
720 | keysIn = keysIn(ind,:); |
---|
721 | % ...and sorting |
---|
722 | [so,ind] = sort(so); |
---|
723 | eo = eo(ind); |
---|
724 | keysIn = keysIn(ind,:); |
---|
725 | |
---|
726 | % Refined data - datain |
---|
727 | datain = []; |
---|
728 | |
---|
729 | for ii=1:nExistingKeys % go through all the existing keys |
---|
730 | if ii==1 |
---|
731 | from = 1; % from byte-offset of original data (dataout) |
---|
732 | else |
---|
733 | from = eo(ii-1)+1; |
---|
734 | end |
---|
735 | to = so(ii)-1; % to byte-offset of original data (dataout) |
---|
736 | |
---|
737 | if ~isempty(dataout) |
---|
738 | datain = [datain dataout(from:to)]; % the lines before the key |
---|
739 | end |
---|
740 | end |
---|
741 | from = eo(ii)+1; |
---|
742 | to = length(dataout); |
---|
743 | if from < to |
---|
744 | datain = [datain dataout(from:to)]; |
---|
745 | end |
---|
746 | end |
---|
747 | |
---|
748 | fwrite(fh,datain,'char'); |
---|
749 | catch |
---|
750 | fclose(fh); |
---|
751 | error(['Error deleting keys from file: ''' fileName ''' : ' lasterr]); |
---|
752 | end |
---|
753 | fclose(fh); |
---|
754 | %------------------------------------ |
---|
755 | |
---|
756 | |
---|
757 | |
---|
758 | |
---|
759 | %------------------------------------ |
---|
760 | function [keys,sections,subsections] = readallkeys(fileName) |
---|
761 | % Reads all the keys out as well as the sections and subsections |
---|
762 | |
---|
763 | keys = []; |
---|
764 | sections = []; |
---|
765 | subsections = []; |
---|
766 | % Read the whole file's contents out |
---|
767 | try |
---|
768 | dataout = textread(fileName,'%s','delimiter','\n'); |
---|
769 | catch |
---|
770 | error(['File: ''' fileName ''' does not exist or can not be opened.']); |
---|
771 | end |
---|
772 | nLines = size(dataout,1); |
---|
773 | |
---|
774 | % Go through all the lines and construct the keys variable |
---|
775 | keys = cell(nLines,4); |
---|
776 | sections = cell(nLines,1); |
---|
777 | subsections = cell(nLines,2); |
---|
778 | keyN = 0; |
---|
779 | secN = 0; |
---|
780 | subsecN = 0; |
---|
781 | secStr = ''; |
---|
782 | subsecStr = ''; |
---|
783 | for ii=1:nLines |
---|
784 | [status,value,key] = processiniline(dataout{ii}); |
---|
785 | if status == 1 |
---|
786 | secN = secN + 1; |
---|
787 | secStr = value; |
---|
788 | sections(secN) = {secStr}; |
---|
789 | elseif status == 2 |
---|
790 | subsecN = subsecN + 1; |
---|
791 | subsecStr = value; |
---|
792 | subsections(subsecN,:) = {secStr,subsecStr}; |
---|
793 | elseif status == 3 |
---|
794 | keyN = keyN + 1; |
---|
795 | keys(keyN,:) = {secStr,subsecStr,key,value}; |
---|
796 | end |
---|
797 | end |
---|
798 | keys(keyN+1:end,:) = []; |
---|
799 | sections(secN+1:end,:) = []; |
---|
800 | subsections(subsecN+1:end,:) = []; |
---|
801 | %------------------------------------ |
---|
802 | |
---|
803 | |
---|
804 | |
---|
805 | %------------------------------------ |
---|
806 | function [status,value,key] = processiniline(line) |
---|
807 | % Processes a line read from the ini file and |
---|
808 | % returns the following values: |
---|
809 | % - status: -1 => unknown string found |
---|
810 | % 0 => empty line found |
---|
811 | % 1 => section found |
---|
812 | % 2 => subsection found |
---|
813 | % 3 => key-value pair found |
---|
814 | % 4 => comment line found (starting with ;) |
---|
815 | % - value: value-string of a key, section, subsection, comment, or unknown string |
---|
816 | % - key: key as string |
---|
817 | |
---|
818 | status = 0; |
---|
819 | value = []; |
---|
820 | key = []; |
---|
821 | line = strim(line); % removes any leading and trailing spaces |
---|
822 | if isempty(line) % empty line |
---|
823 | return |
---|
824 | end |
---|
825 | if strcmpi(line(1),';') % comment found |
---|
826 | status = 4; |
---|
827 | value = line(2:end); |
---|
828 | elseif (line(1) == '[') & (line(end) == ']') & (length(line) >= 3) % section found |
---|
829 | value = lower(line(2:end-1)); |
---|
830 | status = 1; |
---|
831 | elseif (line(1) == '{') &... % subsection found |
---|
832 | (line(end) == '}') & (length(line) >= 3) |
---|
833 | value = lower(line(2:end-1)); |
---|
834 | status = 2; |
---|
835 | else % either key-value pair or unknown string |
---|
836 | pos = findstr(line,'='); |
---|
837 | if ~isempty(pos) % key-value pair found |
---|
838 | status = 3; |
---|
839 | key = lower(line(1:pos-1)); |
---|
840 | value = line(pos+1:end); |
---|
841 | key = strim(key); % removes any leading and trailing spaces |
---|
842 | value = strim(value); % removes any leading and trailing spaces |
---|
843 | if isempty(key) % empty keys are not allowed |
---|
844 | status = 0; |
---|
845 | key = []; |
---|
846 | value = []; |
---|
847 | end |
---|
848 | else % unknown string found |
---|
849 | status = -1; |
---|
850 | value = line; |
---|
851 | end |
---|
852 | end |
---|
853 | |
---|
854 | |
---|
855 | %------------------------------------ |
---|
856 | function outstr = strim(str) |
---|
857 | % Removes leading and trailing spaces (spaces, tabs, endlines,...) |
---|
858 | % from the str string. |
---|
859 | if isnumeric(str); |
---|
860 | outstr = str; |
---|
861 | return |
---|
862 | end |
---|
863 | ind = find( ~isspace(str) ); % indices of the non-space characters in the str |
---|
864 | if isempty(ind) |
---|
865 | outstr = []; |
---|
866 | else |
---|
867 | outstr = str( ind(1):ind(end) ); |
---|
868 | end |
---|
869 | |
---|
870 | |
---|
871 | |
---|
872 | %------------------------------------ |
---|
873 | function cs = cellstrings(m,n) |
---|
874 | % Creates a m x n cell array of empty strings - '' |
---|
875 | cs = cell(m,n); |
---|
876 | cs(:) = {''}; |
---|
877 | |
---|
878 | |
---|
879 | %------------------------------------ |
---|
880 | function y = n2s(x) |
---|
881 | % Converts numeric matrix to string representation. |
---|
882 | % Example: x given as [1 2;3 4] returns y = '1,2;3;4' |
---|
883 | if ischar(x) | isempty(x) |
---|
884 | y = x; |
---|
885 | return |
---|
886 | end |
---|
887 | [m,n] = size(x); |
---|
888 | y = [num2str(x(1,:),'%15.6g')]; |
---|
889 | for ii=2:m |
---|
890 | y = [y ';' num2str(x(ii,:),'%15.6g')]; |
---|
891 | end |
---|