Mercurial > hg > octave-nkf
annotate src/ls-oct-ascii.cc @ 8946:e7e928088e90
fix CRLF issues with text-mode reading in windows when loading ascii data
author | Benjamin Lindner <lindnerb@users.sourceforge.net> |
---|---|
date | Tue, 10 Mar 2009 01:01:50 -0400 |
parents | eb63fbe60fab |
children | af43309a59f4 |
rev | line source |
---|---|
4634 | 1 /* |
2 | |
8920 | 3 Copyright (C) 1996, 1997, 2003, 2004, 2005, 2006, 2007, 2008 John W. Eaton |
4634 | 4 |
5 This file is part of Octave. | |
6 | |
7 Octave is free software; you can redistribute it and/or modify it | |
8 under the terms of the GNU General Public License as published by the | |
7016 | 9 Free Software Foundation; either version 3 of the License, or (at your |
10 option) any later version. | |
4634 | 11 |
12 Octave is distributed in the hope that it will be useful, but WITHOUT | |
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 for more details. | |
16 | |
17 You should have received a copy of the GNU General Public License | |
7016 | 18 along with Octave; see the file COPYING. If not, see |
19 <http://www.gnu.org/licenses/>. | |
4634 | 20 |
21 */ | |
22 | |
23 // Author: John W. Eaton. | |
24 | |
25 #ifdef HAVE_CONFIG_H | |
26 #include <config.h> | |
27 #endif | |
28 | |
29 #include <cstring> | |
30 #include <cctype> | |
31 | |
32 #include <fstream> | |
33 #include <iomanip> | |
34 #include <iostream> | |
5765 | 35 #include <sstream> |
4634 | 36 #include <string> |
37 | |
38 #include "byte-swap.h" | |
39 #include "data-conv.h" | |
40 #include "file-ops.h" | |
41 #include "glob-match.h" | |
42 #include "lo-mappers.h" | |
43 #include "mach-info.h" | |
44 #include "oct-env.h" | |
45 #include "oct-time.h" | |
46 #include "quit.h" | |
47 #include "str-vec.h" | |
48 | |
49 #include "Cell.h" | |
50 #include "defun.h" | |
51 #include "error.h" | |
52 #include "gripes.h" | |
53 #include "load-save.h" | |
8946
e7e928088e90
fix CRLF issues with text-mode reading in windows when loading ascii data
Benjamin Lindner <lindnerb@users.sourceforge.net>
parents:
8920
diff
changeset
|
54 #include "ls-ascii-helper.h" |
e7e928088e90
fix CRLF issues with text-mode reading in windows when loading ascii data
Benjamin Lindner <lindnerb@users.sourceforge.net>
parents:
8920
diff
changeset
|
55 #include "ls-oct-ascii.h" |
4634 | 56 #include "oct-obj.h" |
57 #include "oct-map.h" | |
58 #include "ov-cell.h" | |
59 #include "pager.h" | |
60 #include "pt-exp.h" | |
61 #include "unwind-prot.h" | |
62 #include "utils.h" | |
63 #include "variables.h" | |
64 #include "version.h" | |
65 #include "dMatrix.h" | |
66 | |
67 // The number of decimal digits to use when writing ascii data. | |
5951 | 68 static int Vsave_precision = 16; |
4634 | 69 |
70 // Functions for reading ascii data. | |
71 | |
72 // Extract a KEYWORD and its value from stream IS, returning the | |
73 // associated value in a new string. | |
74 // | |
75 // Input should look something like: | |
76 // | |
77 // [%#][ \t]*keyword[ \t]*:[ \t]*string-value[ \t]*\n | |
78 | |
79 std::string | |
4687 | 80 extract_keyword (std::istream& is, const char *keyword, const bool next_only) |
4634 | 81 { |
82 std::string retval; | |
83 | |
7744
14b841c47a5f
handle load/save for handles to built-in functions
John W. Eaton <jwe@octave.org>
parents:
7336
diff
changeset
|
84 int ch = is.peek (); |
14b841c47a5f
handle load/save for handles to built-in functions
John W. Eaton <jwe@octave.org>
parents:
7336
diff
changeset
|
85 if (next_only && ch != '%' && ch != '#') |
14b841c47a5f
handle load/save for handles to built-in functions
John W. Eaton <jwe@octave.org>
parents:
7336
diff
changeset
|
86 return retval; |
14b841c47a5f
handle load/save for handles to built-in functions
John W. Eaton <jwe@octave.org>
parents:
7336
diff
changeset
|
87 |
4634 | 88 char c; |
89 while (is.get (c)) | |
90 { | |
91 if (c == '%' || c == '#') | |
92 { | |
5765 | 93 std::ostringstream buf; |
4634 | 94 |
95 while (is.get (c) && (c == ' ' || c == '\t' || c == '%' || c == '#')) | |
96 ; // Skip whitespace and comment characters. | |
97 | |
98 if (isalpha (c)) | |
99 buf << c; | |
100 | |
101 while (is.get (c) && isalpha (c)) | |
102 buf << c; | |
103 | |
5765 | 104 std::string tmp = buf.str (); |
5679 | 105 bool match = (tmp.compare (0, strlen (keyword), keyword) == 0); |
4634 | 106 |
107 if (match) | |
108 { | |
5765 | 109 std::ostringstream value; |
4634 | 110 while (is.get (c) && (c == ' ' || c == '\t' || c == ':')) |
111 ; // Skip whitespace and the colon. | |
112 | |
8087
7d19f4f70c61
ls-oct-ascii.{h,cc} (extract_keyword): accept \r as line ending character
John W. Eaton <jwe@octave.org>
parents:
8021
diff
changeset
|
113 if (c != '\n' && c != '\r') |
4634 | 114 { |
115 value << c; | |
8087
7d19f4f70c61
ls-oct-ascii.{h,cc} (extract_keyword): accept \r as line ending character
John W. Eaton <jwe@octave.org>
parents:
8021
diff
changeset
|
116 while (is.get (c) && c != '\n' && c != '\r') |
4634 | 117 value << c; |
118 } | |
5765 | 119 |
120 retval = value.str (); | |
4634 | 121 break; |
122 } | |
4687 | 123 else if (next_only) |
124 break; | |
6202 | 125 else |
8946
e7e928088e90
fix CRLF issues with text-mode reading in windows when loading ascii data
Benjamin Lindner <lindnerb@users.sourceforge.net>
parents:
8920
diff
changeset
|
126 skip_until_newline (is, false); |
4634 | 127 } |
128 } | |
129 | |
130 int len = retval.length (); | |
131 | |
132 if (len > 0) | |
133 { | |
134 while (len) | |
135 { | |
136 c = retval[len-1]; | |
137 | |
138 if (c == ' ' || c == '\t') | |
139 len--; | |
140 else | |
141 { | |
142 retval.resize (len); | |
143 break; | |
144 } | |
145 } | |
146 } | |
147 | |
148 return retval; | |
149 } | |
150 | |
151 // Extract one value (scalar, matrix, string, etc.) from stream IS and | |
152 // place it in TC, returning the name of the variable. If the value | |
153 // is tagged as global in the file, return TRUE in GLOBAL. | |
154 // | |
4687 | 155 // Each type supplies its own function to load the data, and so this |
156 // function is extensible. | |
157 // | |
4634 | 158 // FILENAME is used for error messages. |
159 // | |
160 // The data is expected to be in the following format: | |
161 // | |
162 // The input file must have a header followed by some data. | |
163 // | |
164 // All lines in the header must begin with a `#' character. | |
165 // | |
166 // The header must contain a list of keyword and value pairs with the | |
167 // keyword and value separated by a colon. | |
168 // | |
169 // Keywords must appear in the following order: | |
170 // | |
171 // # name: <name> | |
172 // # type: <type> | |
173 // # <info> | |
174 // | |
4687 | 175 // Where, for the built in types are: |
4634 | 176 // |
177 // <name> : a valid identifier | |
178 // | |
179 // <type> : <typename> | |
180 // | global <typename> | |
181 // | |
182 // <typename> : scalar | |
183 // | complex scalar | |
184 // | matrix | |
185 // | complex matrix | |
4687 | 186 // | bool |
187 // | bool matrix | |
4634 | 188 // | string |
189 // | range | |
190 // | |
191 // <info> : <matrix info> | |
192 // | <string info> | |
193 // | |
194 // <matrix info> : # rows: <integer> | |
195 // : # columns: <integer> | |
196 // | |
4687 | 197 // <string info> : # elements: <integer> |
198 // : # length: <integer> (once before each string) | |
4634 | 199 // |
4687 | 200 // For backward compatibility the type "string array" is treated as a |
201 // "string" type. Also "string" can have a single element with no elements | |
202 // line such that | |
203 // | |
204 // <string info> : # length: <integer> | |
4634 | 205 // |
206 // Formatted ASCII data follows the header. | |
207 // | |
208 // Example: | |
209 // | |
210 // # name: foo | |
211 // # type: matrix | |
212 // # rows: 2 | |
213 // # columns: 2 | |
214 // 2 4 | |
215 // 1 3 | |
216 // | |
217 // Example: | |
218 // | |
219 // # name: foo | |
4687 | 220 // # type: string |
4634 | 221 // # elements: 5 |
222 // # length: 4 | |
223 // this | |
224 // # length: 2 | |
225 // is | |
226 // # length: 1 | |
227 // a | |
228 // # length: 6 | |
229 // string | |
230 // # length: 5 | |
231 // array | |
232 // | |
5775 | 233 // FIXME -- this format is fairly rigid, and doesn't allow for |
4687 | 234 // arbitrary comments. Someone should fix that. It does allow arbitrary |
235 // types however. | |
4634 | 236 |
237 // Ugh. The signature of the compare method is not standard in older | |
238 // versions of the GNU libstdc++. Do this instead: | |
239 | |
240 #define SUBSTRING_COMPARE_EQ(s, pos, n, t) (s.substr (pos, n) == t) | |
241 | |
242 std::string | |
243 read_ascii_data (std::istream& is, const std::string& filename, bool& global, | |
5754 | 244 octave_value& tc, octave_idx_type count) |
4634 | 245 { |
246 // Read name for this entry or break on EOF. | |
247 | |
248 std::string name = extract_keyword (is, "name"); | |
249 | |
250 if (name.empty ()) | |
251 { | |
252 if (count == 0) | |
253 error ("load: empty name keyword or no data found in file `%s'", | |
254 filename.c_str ()); | |
255 | |
256 return std::string (); | |
257 } | |
258 | |
7744
14b841c47a5f
handle load/save for handles to built-in functions
John W. Eaton <jwe@octave.org>
parents:
7336
diff
changeset
|
259 if (! (name == ".nargin." || name == ".nargout." |
14b841c47a5f
handle load/save for handles to built-in functions
John W. Eaton <jwe@octave.org>
parents:
7336
diff
changeset
|
260 || name == CELL_ELT_TAG || valid_identifier (name))) |
4634 | 261 { |
262 error ("load: bogus identifier `%s' found in file `%s'", | |
263 name.c_str (), filename.c_str ()); | |
264 return std::string (); | |
265 } | |
266 | |
267 // Look for type keyword. | |
268 | |
269 std::string tag = extract_keyword (is, "type"); | |
270 | |
271 if (! tag.empty ()) | |
272 { | |
273 std::string typ; | |
274 size_t pos = tag.rfind (' '); | |
275 | |
8021 | 276 if (pos != std::string::npos) |
4634 | 277 { |
278 global = SUBSTRING_COMPARE_EQ (tag, 0, 6, "global"); | |
279 | |
280 typ = global ? tag.substr (7) : tag; | |
281 } | |
282 else | |
283 typ = tag; | |
284 | |
4687 | 285 // Special case for backward compatiablity. A small bit of cruft |
286 if (SUBSTRING_COMPARE_EQ (typ, 0, 12, "string array")) | |
287 tc = octave_value (charMatrix (), true); | |
288 else | |
289 tc = octave_value_typeinfo::lookup_type (typ); | |
4634 | 290 |
4988 | 291 if (! tc.load_ascii (is)) |
292 error ("load: trouble reading ascii file `%s'", filename.c_str ()); | |
4634 | 293 } |
294 else | |
295 error ("load: failed to extract keyword specifying value type"); | |
296 | |
297 if (error_state) | |
298 { | |
299 error ("load: reading file %s", filename.c_str ()); | |
300 return std::string (); | |
301 } | |
302 | |
303 return name; | |
304 } | |
305 | |
306 // Save the data from TC along with the corresponding NAME, and global | |
307 // flag MARK_AS_GLOBAL on stream OS in the plain text format described | |
308 // above for load_ascii_data. If NAME is empty, the name: line is not | |
309 // generated. PRECISION specifies the number of decimal digits to print. | |
310 // | |
311 // Assumes ranges and strings cannot contain Inf or NaN values. | |
312 // | |
313 // Returns 1 for success and 0 for failure. | |
314 | |
5775 | 315 // FIXME -- should probably write the help string here too. |
4634 | 316 |
317 bool | |
318 save_ascii_data (std::ostream& os, const octave_value& val_arg, | |
6974 | 319 const std::string& name, bool mark_as_global, |
320 int precision) | |
4634 | 321 { |
322 bool success = true; | |
323 | |
324 if (! name.empty ()) | |
325 os << "# name: " << name << "\n"; | |
326 | |
327 octave_value val = val_arg; | |
328 | |
4687 | 329 if (mark_as_global) |
330 os << "# type: global " << val.type_name () << "\n"; | |
331 else | |
332 os << "# type: " << val.type_name() << "\n"; | |
4634 | 333 |
5951 | 334 if (! precision) |
335 precision = Vsave_precision; | |
336 | |
337 long old_precision = os.precision (); | |
338 os.precision (precision); | |
339 | |
6974 | 340 success = val.save_ascii (os); |
4634 | 341 |
342 os.precision (old_precision); | |
343 | |
344 return (os && success); | |
345 } | |
346 | |
347 bool | |
348 save_ascii_data_for_plotting (std::ostream& os, const octave_value& t, | |
349 const std::string& name) | |
350 { | |
6974 | 351 return save_ascii_data (os, t, name, false, 6); |
4634 | 352 } |
353 | |
354 // Maybe this should be a static function in tree-plot.cc? | |
355 | |
356 // If TC is matrix, save it on stream OS in a format useful for | |
357 // making a 3-dimensional plot with gnuplot. If PARAMETRIC is | |
358 // TRUE, assume a parametric 3-dimensional plot will be generated. | |
359 | |
360 bool | |
361 save_three_d (std::ostream& os, const octave_value& tc, bool parametric) | |
362 { | |
363 bool fail = false; | |
364 | |
5275 | 365 octave_idx_type nr = tc.rows (); |
366 octave_idx_type nc = tc.columns (); | |
4634 | 367 |
368 if (tc.is_real_matrix ()) | |
369 { | |
370 os << "# 3D data...\n" | |
371 << "# type: matrix\n" | |
372 << "# total rows: " << nr << "\n" | |
373 << "# total columns: " << nc << "\n"; | |
374 | |
6171 | 375 long old_precision = os.precision (); |
6257 | 376 os.precision (6); |
6171 | 377 |
4634 | 378 if (parametric) |
379 { | |
5275 | 380 octave_idx_type extras = nc % 3; |
4634 | 381 if (extras) |
382 warning ("ignoring last %d columns", extras); | |
383 | |
384 Matrix tmp = tc.matrix_value (); | |
385 nr = tmp.rows (); | |
386 | |
5275 | 387 for (octave_idx_type i = 0; i < nc-extras; i += 3) |
4634 | 388 { |
389 os << tmp.extract (0, i, nr-1, i+2); | |
390 if (i+3 < nc-extras) | |
391 os << "\n"; | |
392 } | |
393 } | |
394 else | |
395 { | |
396 Matrix tmp = tc.matrix_value (); | |
397 nr = tmp.rows (); | |
398 | |
5275 | 399 for (octave_idx_type i = 0; i < nc; i++) |
4634 | 400 { |
401 os << tmp.extract (0, i, nr-1, i); | |
402 if (i+1 < nc) | |
403 os << "\n"; | |
404 } | |
405 } | |
6171 | 406 |
407 os.precision (old_precision); | |
4634 | 408 } |
409 else | |
410 { | |
411 ::error ("for now, I can only save real matrices in 3D format"); | |
412 fail = true; | |
413 } | |
414 | |
415 return (os && ! fail); | |
416 } | |
417 | |
5794 | 418 DEFUN (save_precision, args, nargout, |
419 "-*- texinfo -*-\n\ | |
420 @deftypefn {Built-in Function} {@var{val} =} save_precision ()\n\ | |
421 @deftypefnx {Built-in Function} {@var{old_val} =} save_precision (@var{new_val})\n\ | |
422 Query or set the internal variable that specifies the number of\n\ | |
423 digits to keep when saving data in text format.\n\ | |
424 @end deftypefn") | |
4634 | 425 { |
5794 | 426 return SET_INTERNAL_VARIABLE_WITH_LIMITS (save_precision, -1, INT_MAX); |
4634 | 427 } |
428 | |
429 /* | |
430 ;;; Local Variables: *** | |
431 ;;; mode: C++ *** | |
432 ;;; End: *** | |
433 */ | |
434 |