GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
ls-oct-ascii.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 1996-2013 John W. Eaton
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
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
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
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
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>
35 #include <sstream>
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"
54 #include "ls-ascii-helper.h"
55 #include "ls-oct-ascii.h"
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.
68 static int Vsave_precision = 16;
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
80 extract_keyword (std::istream& is, const char *keyword, const bool next_only)
81 {
82  std::string retval;
83 
84  int ch = is.peek ();
85  if (next_only && ch != '%' && ch != '#')
86  return retval;
87 
88  char c;
89  while (is.get (c))
90  {
91  if (c == '%' || c == '#')
92  {
93  std::ostringstream buf;
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 
104  std::string tmp = buf.str ();
105  bool match = (tmp.compare (0, strlen (keyword), keyword) == 0);
106 
107  if (match)
108  {
109  std::ostringstream value;
110  while (is.get (c) && (c == ' ' || c == '\t' || c == ':'))
111  ; // Skip whitespace and the colon.
112 
113  is.putback (c);
114  retval = read_until_newline (is, false);
115  break;
116  }
117  else if (next_only)
118  break;
119  else
120  skip_until_newline (is, false);
121  }
122  }
123 
124  int len = retval.length ();
125 
126  if (len > 0)
127  {
128  while (len)
129  {
130  c = retval[len-1];
131 
132  if (c == ' ' || c == '\t')
133  len--;
134  else
135  {
136  retval.resize (len);
137  break;
138  }
139  }
140  }
141 
142  return retval;
143 }
144 
145 // Extract one value (scalar, matrix, string, etc.) from stream IS and
146 // place it in TC, returning the name of the variable. If the value
147 // is tagged as global in the file, return TRUE in GLOBAL.
148 //
149 // Each type supplies its own function to load the data, and so this
150 // function is extensible.
151 //
152 // FILENAME is used for error messages.
153 //
154 // The data is expected to be in the following format:
155 //
156 // The input file must have a header followed by some data.
157 //
158 // All lines in the header must begin with a '#' character.
159 //
160 // The header must contain a list of keyword and value pairs with the
161 // keyword and value separated by a colon.
162 //
163 // Keywords must appear in the following order:
164 //
165 // # name: <name>
166 // # type: <type>
167 // # <info>
168 //
169 // Where, for the built in types are:
170 //
171 // <name> : a valid identifier
172 //
173 // <type> : <typename>
174 // | global <typename>
175 //
176 // <typename> : scalar
177 // | complex scalar
178 // | matrix
179 // | complex matrix
180 // | bool
181 // | bool matrix
182 // | string
183 // | range
184 //
185 // <info> : <matrix info>
186 // | <string info>
187 //
188 // <matrix info> : # rows: <integer>
189 // : # columns: <integer>
190 //
191 // <string info> : # elements: <integer>
192 // : # length: <integer> (once before each string)
193 //
194 // For backward compatibility the type "string array" is treated as a
195 // "string" type. Also "string" can have a single element with no elements
196 // line such that
197 //
198 // <string info> : # length: <integer>
199 //
200 // Formatted ASCII data follows the header.
201 //
202 // Example:
203 //
204 // # name: foo
205 // # type: matrix
206 // # rows: 2
207 // # columns: 2
208 // 2 4
209 // 1 3
210 //
211 // Example:
212 //
213 // # name: foo
214 // # type: string
215 // # elements: 5
216 // # length: 4
217 // this
218 // # length: 2
219 // is
220 // # length: 1
221 // a
222 // # length: 6
223 // string
224 // # length: 5
225 // array
226 //
227 // FIXME: this format is fairly rigid, and doesn't allow for
228 // arbitrary comments. Someone should fix that. It does allow arbitrary
229 // types however.
230 
231 // Ugh. The signature of the compare method is not standard in older
232 // versions of the GNU libstdc++. Do this instead:
233 
234 #define SUBSTRING_COMPARE_EQ(s, pos, n, t) (s.substr (pos, n) == t)
235 
236 std::string
237 read_ascii_data (std::istream& is, const std::string& filename, bool& global,
238  octave_value& tc, octave_idx_type count)
239 {
240  // Read name for this entry or break on EOF.
241 
242  std::string name = extract_keyword (is, "name");
243 
244  if (name.empty ())
245  {
246  if (count == 0)
247  error ("load: empty name keyword or no data found in file '%s'",
248  filename.c_str ());
249 
250  return std::string ();
251  }
252 
253  if (! (name == ".nargin." || name == ".nargout."
254  || name == CELL_ELT_TAG || valid_identifier (name)))
255  {
256  error ("load: bogus identifier '%s' found in file '%s'",
257  name.c_str (), filename.c_str ());
258  return std::string ();
259  }
260 
261  // Look for type keyword.
262 
263  std::string tag = extract_keyword (is, "type");
264 
265  if (! tag.empty ())
266  {
267  std::string typ;
268  size_t pos = tag.rfind (' ');
269 
270  if (pos != std::string::npos)
271  {
272  global = SUBSTRING_COMPARE_EQ (tag, 0, 6, "global");
273 
274  typ = global ? tag.substr (7) : tag;
275  }
276  else
277  typ = tag;
278 
279  // Special case for backward compatiablity. A small bit of cruft
280  if (SUBSTRING_COMPARE_EQ (typ, 0, 12, "string array"))
281  tc = charMatrix ();
282  else
284 
285  if (! tc.load_ascii (is))
286  error ("load: trouble reading ascii file '%s'", filename.c_str ());
287  }
288  else
289  error ("load: failed to extract keyword specifying value type");
290 
291  if (error_state)
292  {
293  error ("load: reading file %s", filename.c_str ());
294  return std::string ();
295  }
296 
297  return name;
298 }
299 
300 // Save the data from TC along with the corresponding NAME, and global
301 // flag MARK_AS_GLOBAL on stream OS in the plain text format described
302 // above for load_ascii_data. If NAME is empty, the name: line is not
303 // generated. PRECISION specifies the number of decimal digits to print.
304 //
305 // Assumes ranges and strings cannot contain Inf or NaN values.
306 //
307 // Returns 1 for success and 0 for failure.
308 
309 // FIXME: should probably write the help string here too.
310 
311 bool
312 save_ascii_data (std::ostream& os, const octave_value& val_arg,
313  const std::string& name, bool mark_as_global,
314  int precision)
315 {
316  bool success = true;
317 
318  if (! name.empty ())
319  os << "# name: " << name << "\n";
320 
321  octave_value val = val_arg;
322 
323  if (mark_as_global)
324  os << "# type: global " << val.type_name () << "\n";
325  else
326  os << "# type: " << val.type_name () << "\n";
327 
328  if (! precision)
329  precision = Vsave_precision;
330 
331  long old_precision = os.precision ();
332  os.precision (precision);
333 
334  success = val.save_ascii (os);
335 
336  // Insert an extra pair of newline characters after the data so that
337  // multiple data elements may be handled separately by gnuplot (see
338  // the description of the index qualifier for the plot command in the
339  // gnuplot documentation).
340  os << "\n\n";
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 {
351  return save_ascii_data (os, t, name, false, 6);
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 
365  octave_idx_type nr = tc.rows ();
366  octave_idx_type nc = tc.columns ();
367 
368  if (tc.is_real_matrix ())
369  {
370  os << "# 3-D data...\n"
371  << "# type: matrix\n"
372  << "# total rows: " << nr << "\n"
373  << "# total columns: " << nc << "\n";
374 
375  long old_precision = os.precision ();
376  os.precision (6);
377 
378  if (parametric)
379  {
380  octave_idx_type extras = nc % 3;
381  if (extras)
382  warning ("ignoring last %d columns", extras);
383 
384  Matrix tmp = tc.matrix_value ();
385  nr = tmp.rows ();
386 
387  for (octave_idx_type i = 0; i < nc-extras; i += 3)
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 
399  for (octave_idx_type i = 0; i < nc; i++)
400  {
401  os << tmp.extract (0, i, nr-1, i);
402  if (i+1 < nc)
403  os << "\n";
404  }
405  }
406 
407  os.precision (old_precision);
408  }
409  else
410  {
411  ::error ("for now, I can only save real matrices in 3-D format");
412  fail = true;
413  }
414 
415  return (os && ! fail);
416 }
417 
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 @deftypefnx {Built-in Function} {} save_precision (@var{new_val}, \"local\")\n\
423 Query or set the internal variable that specifies the number of\n\
424 digits to keep when saving data in text format.\n\
425 \n\
426 When called from inside a function with the @qcode{\"local\"} option, the\n\
427 variable is changed locally for the function and any subroutines it calls.\n\
428 The original variable value is restored when exiting the function.\n\
429 @end deftypefn")
430 {
431  return SET_INTERNAL_VARIABLE_WITH_LIMITS (save_precision, -1,
433 }