GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
ls-oct-text.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <cstring>
31 #include <cctype>
32 
33 #include <fstream>
34 #include <iomanip>
35 #include <istream>
36 #include <ostream>
37 #include <sstream>
38 #include <string>
39 
40 #include "byte-swap.h"
41 #include "data-conv.h"
42 #include "file-ops.h"
43 #include "glob-match.h"
44 #include "lo-mappers.h"
45 #include "mach-info.h"
46 #include "oct-env.h"
47 #include "oct-time.h"
48 #include "quit.h"
49 #include "str-vec.h"
50 
51 #include "Cell.h"
52 #include "defun.h"
53 #include "error.h"
54 #include "errwarn.h"
55 #include "interpreter.h"
56 #include "interpreter-private.h"
57 #include "load-save.h"
58 #include "ls-ascii-helper.h"
59 #include "ls-oct-text.h"
60 #include "ovl.h"
61 #include "oct-map.h"
62 #include "ov-cell.h"
63 #include "pager.h"
64 #include "unwind-prot.h"
65 #include "utils.h"
66 #include "variables.h"
67 #include "version.h"
68 #include "dMatrix.h"
69 
70 // The number of decimal digits to use when writing ASCII data.
71 // 17 is the minimum necessary for lossless save/restore of IEEE-754 doubles.
72 static int Vsave_precision = 17;
73 
74 // Functions for reading octave format text data.
75 
76 // Extract a KEYWORD and its value from stream IS, returning the
77 // associated value in a new string.
78 //
79 // Input should look something like:
80 //
81 // [%#][ \t]*keyword[ \t]*:[ \t]*string-value[ \t]*\n
82 
83 std::string
84 extract_keyword (std::istream& is, const char *keyword, const bool next_only)
85 {
86  std::string retval;
87 
88  int ch = is.peek ();
89  if (next_only && ch != '%' && ch != '#')
90  return retval;
91 
92  char c;
93  while (is.get (c))
94  {
95  if (c == '%' || c == '#')
96  {
97  std::ostringstream buf;
98 
99  while (is.get (c) && (c == ' ' || c == '\t' || c == '%' || c == '#'))
100  ; // Skip whitespace and comment characters.
101 
102  if (isalpha (c))
103  buf << c;
104 
105  while (is.get (c) && isalpha (c))
106  buf << c;
107 
108  std::string tmp = buf.str ();
109  bool match = (tmp.substr (0, strlen (keyword)) == keyword);
110 
111  if (match)
112  {
113  std::ostringstream value;
114  while (is.get (c) && (c == ' ' || c == '\t' || c == ':'))
115  ; // Skip whitespace and the colon.
116 
117  is.putback (c);
118  retval = read_until_newline (is, false);
119  break;
120  }
121  else if (next_only)
122  break;
123  else
124  skip_until_newline (is, false);
125  }
126  }
127 
128  int len = retval.length ();
129 
130  if (len > 0)
131  {
132  while (len)
133  {
134  c = retval[len-1];
135 
136  if (c == ' ' || c == '\t')
137  len--;
138  else
139  {
140  retval.resize (len);
141  break;
142  }
143  }
144  }
145 
146  return retval;
147 }
148 
149 // Extract one value (scalar, matrix, string, etc.) from stream IS and
150 // place it in TC, returning the name of the variable. If the value
151 // is tagged as global in the file, return TRUE in GLOBAL.
152 //
153 // Each type supplies its own function to load the data, and so this
154 // function is extensible.
155 //
156 // FILENAME is used for error messages.
157 //
158 // The data is expected to be in the following format:
159 //
160 // The input file must have a header followed by some data.
161 //
162 // All lines in the header must begin with a '#' character.
163 //
164 // The header must contain a list of keyword and value pairs with the
165 // keyword and value separated by a colon.
166 //
167 // Keywords must appear in the following order:
168 //
169 // # name: <name>
170 // # type: <type>
171 // # <info>
172 //
173 // Where, for the built in types are:
174 //
175 // <name> : a valid identifier
176 //
177 // <type> : <typename>
178 // | global <typename>
179 //
180 // <typename> : scalar
181 // | complex scalar
182 // | matrix
183 // | complex matrix
184 // | bool
185 // | bool matrix
186 // | string
187 // | range
188 //
189 // <info> : <matrix info>
190 // | <string info>
191 //
192 // <matrix info> : # rows: <integer>
193 // : # columns: <integer>
194 //
195 // <string info> : # elements: <integer>
196 // : # length: <integer> (once before each string)
197 //
198 // For backward compatibility the type "string array" is treated as a
199 // "string" type. Also "string" can have a single element with no elements
200 // line such that
201 //
202 // <string info> : # length: <integer>
203 //
204 // Formatted ASCII data follows the header.
205 //
206 // Example:
207 //
208 // # name: foo
209 // # type: matrix
210 // # rows: 2
211 // # columns: 2
212 // 2 4
213 // 1 3
214 //
215 // Example:
216 //
217 // # name: foo
218 // # type: string
219 // # elements: 5
220 // # length: 4
221 // this
222 // # length: 2
223 // is
224 // # length: 1
225 // a
226 // # length: 6
227 // string
228 // # length: 5
229 // array
230 //
231 // FIXME: This format is fairly rigid, and doesn't allow for arbitrary comments.
232 // Someone should fix that. It does allow arbitrary types however.
233 
234 // Ugh. The signature of the compare method is not standard in older
235 // versions of the GNU libstdc++. Do this instead:
236 
237 #define SUBSTRING_COMPARE_EQ(s, pos, n, t) (s.substr (pos, n) == (t))
238 
239 static octave_value
240 load_inline_fcn (std::istream& is, const std::string& filename)
241 {
242  int nargs;
243  if (extract_keyword (is, "nargs", nargs, true))
244  {
245  std::string name;
246  octave_value_list args (nargs+1);
247  for (int i = 0; i < nargs; i++)
248  {
249  std::string tmp;
250  is >> tmp;
251  args(i+1) = tmp;
252  }
253  is >> name;
254  if (name == "0")
255  name = "";
256 
258 
259  std::string buf;
260 
261  if (is)
262  {
263 
264  // Get a line of text whitespace characters included,
265  // leaving newline in the stream.
266  buf = read_until_newline (is, true);
267  }
268 
269  if (is)
270  {
271  args(0) = std::string (buf);
272 
273  octave::interpreter& interp
274  = octave::__get_interpreter__ ("load_inline_fcn");
275 
276  octave_value_list tmp = interp.feval ("inline", args, 1);
277 
278  if (tmp.length () > 0)
279  return tmp(0);
280  }
281  }
282 
283  error ("load: trouble reading ascii file '%s'", filename.c_str ());
284 }
285 
286 std::string
287 read_text_data (std::istream& is, const std::string& filename, bool& global,
288  octave_value& tc, octave_idx_type count,
289  const bool do_name_validation)
290 {
291  // Read name for this entry or break on EOF.
292 
293  std::string name = extract_keyword (is, "name");
294 
295  if (name.empty ())
296  {
297  if (count == 0)
298  error ("load: empty name keyword or no data found in file '%s'",
299  filename.c_str ());
300 
301  return "";
302  }
303 
304  if (name != CELL_ELT_TAG
305  && do_name_validation && ! octave::valid_identifier (name))
306  error ("load: invalid identifier '%s' found in file '%s'",
307  name.c_str (), filename.c_str ());
308 
309  // Look for type keyword.
310 
311  std::string tag = extract_keyword (is, "type");
312 
313  if (tag.empty ())
314  error ("load: failed to extract keyword specifying value type");
315 
316  std::string typ;
317  size_t pos = tag.rfind (' ');
318 
319  if (pos != std::string::npos)
320  {
321  global = SUBSTRING_COMPARE_EQ (tag, 0, 6, "global");
322 
323  typ = (global ? tag.substr (7) : tag);
324  }
325  else
326  typ = tag;
327 
328  // Special case for backward compatibility. A small bit of cruft
329  if (SUBSTRING_COMPARE_EQ (typ, 0, 12, "string array"))
330  tc = charMatrix ();
331  else if (SUBSTRING_COMPARE_EQ (typ, 0, 15, "inline function"))
332  {
333  // Special case for loading old octave_inline_fcn objects.
334  tc = load_inline_fcn (is, filename);
335  return name;
336  }
337  else
338  {
339  octave::type_info& type_info
340  = octave::__get_type_info__ ("read_text_data");
341 
342  tc = type_info.lookup_type (typ);
343  }
344 
345  if (! tc.load_ascii (is))
346  error ("load: trouble reading ascii file '%s'", filename.c_str ());
347 
348  return name;
349 }
350 
351 // Save the data from TC along with the corresponding NAME, and global
352 // flag MARK_AS_GLOBAL on stream OS in the plain text format described
353 // above for load_text_data. If NAME is empty, the name: line is not
354 // generated. PRECISION specifies the number of decimal digits to print.
355 //
356 // Assumes ranges and strings cannot contain Inf or NaN values.
357 //
358 // Returns 1 for success and 0 for failure.
359 
360 // FIXME: should probably write the help string here too.
361 
362 bool
363 save_text_data (std::ostream& os, const octave_value& val_arg,
364  const std::string& name, bool mark_global,
365  int precision)
366 {
367  if (! name.empty ())
368  os << "# name: " << name << "\n";
369 
370  octave_value val = val_arg;
371 
372  if (mark_global)
373  os << "# type: global " << val.type_name () << "\n";
374  else
375  os << "# type: " << val.type_name () << "\n";
376 
377  if (! precision)
378  precision = Vsave_precision;
379 
380  long old_precision = os.precision ();
381  os.precision (precision);
382 
383  bool success = val.save_ascii (os);
384 
385  // Insert an extra pair of newline characters after the data so that
386  // multiple data elements may be handled separately by gnuplot (see
387  // the description of the index qualifier for the plot command in the
388  // gnuplot documentation).
389  os << "\n\n";
390 
391  os.precision (old_precision);
392 
393  return (os && success);
394 }
395 
396 bool
397 save_text_data_for_plotting (std::ostream& os, const octave_value& t,
398  const std::string& name)
399 {
400  return save_text_data (os, t, name, false, 6);
401 }
402 
403 // Maybe this should be a static function in tree-plot.cc?
404 
405 // If TC is matrix, save it on stream OS in a format useful for
406 // making a 3-D plot with gnuplot. If PARAMETRIC is TRUE,
407 // assume a parametric 3-D plot will be generated.
408 
409 bool
410 save_three_d (std::ostream& os, const octave_value& tc, bool parametric)
411 {
412  octave_idx_type nr = tc.rows ();
413  octave_idx_type nc = tc.columns ();
414 
415  if (! tc.is_real_matrix ())
416  error ("for now, I can only save real matrices in 3-D format");
417 
418  os << "# 3-D data...\n"
419  << "# type: matrix\n"
420  << "# total rows: " << nr << "\n"
421  << "# total columns: " << nc << "\n";
422 
423  long old_precision = os.precision ();
424  os.precision (6);
425 
426  if (parametric)
427  {
428  octave_idx_type extras = nc % 3;
429  if (extras)
430  warning ("ignoring last %" OCTAVE_IDX_TYPE_FORMAT " columns", extras);
431 
432  Matrix tmp = tc.matrix_value ();
433  nr = tmp.rows ();
434 
435  for (octave_idx_type i = 0; i < nc-extras; i += 3)
436  {
437  os << tmp.extract (0, i, nr-1, i+2);
438  if (i+3 < nc-extras)
439  os << "\n";
440  }
441  }
442  else
443  {
444  Matrix tmp = tc.matrix_value ();
445  nr = tmp.rows ();
446 
447  for (octave_idx_type i = 0; i < nc; i++)
448  {
449  os << tmp.extract (0, i, nr-1, i);
450  if (i+1 < nc)
451  os << "\n";
452  }
453  }
454 
455  os.precision (old_precision);
456 
457  return (static_cast<bool> (os));
458 }
459 
460 DEFUN (save_precision, args, nargout,
461  doc: /* -*- texinfo -*-
462 @deftypefn {} {@var{val} =} save_precision ()
463 @deftypefnx {} {@var{old_val} =} save_precision (@var{new_val})
464 @deftypefnx {} {} save_precision (@var{new_val}, "local")
465 Query or set the internal variable that specifies the number of digits to
466 keep when saving data in text format.
467 
468 The default value is 17 which is the minimum necessary for the lossless saving
469 and restoring of IEEE-754 double values; For IEEE-754 single values the minimum
470 value is 9. If file size is a concern, it is probably better to choose a
471 binary format for saving data rather than to reduce the precision of the saved
472 values.
473 
474 When called from inside a function with the @qcode{"local"} option, the
475 variable is changed locally for the function and any subroutines it calls.
476 The original variable value is restored when exiting the function.
477 
478 @seealso{save_default_options}
479 @end deftypefn */)
480 {
481  return SET_INTERNAL_VARIABLE_WITH_LIMITS (save_precision, -1,
483 }
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Definition: Array.cc:1011
octave_idx_type rows(void) const
Definition: Array.h:415
Definition: dMatrix.h:42
Matrix extract(octave_idx_type r1, octave_idx_type c1, octave_idx_type r2, octave_idx_type c2) const
Definition: dMatrix.cc:397
octave_value_list feval(const char *name, const octave_value_list &args=octave_value_list(), int nargout=0)
Evaluate an Octave function (built-in or interpreted) and return the list of result values.
octave_value lookup_type(const std::string &nm)
Definition: ov-typeinfo.cc:479
octave_idx_type length(void) const
Definition: ovl.h:113
octave_idx_type rows(void) const
Definition: ov.h:504
bool save_ascii(std::ostream &os)
Definition: ov.h:1299
octave_idx_type columns(void) const
Definition: ov.h:506
bool load_ascii(std::istream &is)
Definition: ov.h:1301
bool is_real_matrix(void) const
Definition: ov.h:569
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:806
std::string type_name(void) const
Definition: ov.h:1254
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void warning(const char *fmt,...)
Definition: error.cc:1050
void error(const char *fmt,...)
Definition: error.cc:968
QString name
void skip_until_newline(std::istream &is, bool keep_newline)
std::string read_until_newline(std::istream &is, bool keep_newline)
void skip_preceeding_newline(std::istream &is)
static int Vsave_precision
Definition: ls-oct-text.cc:72
std::string read_text_data(std::istream &is, const std::string &filename, bool &global, octave_value &tc, octave_idx_type count, const bool do_name_validation)
Definition: ls-oct-text.cc:287
bool save_text_data_for_plotting(std::ostream &os, const octave_value &t, const std::string &name)
Definition: ls-oct-text.cc:397
std::string extract_keyword(std::istream &is, const char *keyword, const bool next_only)
Definition: ls-oct-text.cc:84
bool save_text_data(std::ostream &os, const octave_value &val_arg, const std::string &name, bool mark_global, int precision)
Definition: ls-oct-text.cc:363
bool save_three_d(std::ostream &os, const octave_value &tc, bool parametric)
Definition: ls-oct-text.cc:410
#define SUBSTRING_COMPARE_EQ(s, pos, n, t)
Definition: ls-oct-text.cc:237
static octave_value load_inline_fcn(std::istream &is, const std::string &filename)
Definition: ls-oct-text.cc:240
#define CELL_ELT_TAG
Definition: ls-oct-text.h:42
bool valid_identifier(const char *s)
Definition: utils.cc:77
type_info & __get_type_info__(const std::string &who)
interpreter & __get_interpreter__(const std::string &who)
T::size_type strlen(const typename T::value_type *str)
Definition: oct-string.cc:85
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
#define SET_INTERNAL_VARIABLE_WITH_LIMITS(NM, MINVAL, MAXVAL)
Definition: variables.h:109
F77_RET_T len
Definition: xerbla.cc:61