GNU Octave  4.4.1
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-2018 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
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License 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 <https://www.gnu.org/licenses/>.
20 
21 */
22 
23 // Author: John W. Eaton.
24 
25 #if defined (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 "errwarn.h"
53 #include "interpreter-private.h"
54 #include "load-save.h"
55 #include "ls-ascii-helper.h"
56 #include "ls-oct-text.h"
57 #include "ovl.h"
58 #include "oct-map.h"
59 #include "ov-cell.h"
60 #include "pager.h"
61 #include "pt-exp.h"
62 #include "unwind-prot.h"
63 #include "utils.h"
64 #include "variables.h"
65 #include "version.h"
66 #include "dMatrix.h"
67 
68 // The number of decimal digits to use when writing ASCII data.
69 // 17 is the minimum necessary for lossless save/restore of IEEE-754 doubles.
70 static int Vsave_precision = 17;
71 
72 // Functions for reading octave format text data.
73 
74 // Extract a KEYWORD and its value from stream IS, returning the
75 // associated value in a new string.
76 //
77 // Input should look something like:
78 //
79 // [%#][ \t]*keyword[ \t]*:[ \t]*string-value[ \t]*\n
80 
82 extract_keyword (std::istream& is, const char *keyword, const bool next_only)
83 {
85 
86  int ch = is.peek ();
87  if (next_only && ch != '%' && ch != '#')
88  return retval;
89 
90  char c;
91  while (is.get (c))
92  {
93  if (c == '%' || c == '#')
94  {
95  std::ostringstream buf;
96 
97  while (is.get (c) && (c == ' ' || c == '\t' || c == '%' || c == '#'))
98  ; // Skip whitespace and comment characters.
99 
100  if (isalpha (c))
101  buf << c;
102 
103  while (is.get (c) && isalpha (c))
104  buf << c;
105 
106  std::string tmp = buf.str ();
107  bool match = (tmp.substr (0, strlen (keyword)) == keyword);
108 
109  if (match)
110  {
111  std::ostringstream value;
112  while (is.get (c) && (c == ' ' || c == '\t' || c == ':'))
113  ; // Skip whitespace and the colon.
114 
115  is.putback (c);
116  retval = read_until_newline (is, false);
117  break;
118  }
119  else if (next_only)
120  break;
121  else
122  skip_until_newline (is, false);
123  }
124  }
125 
126  int len = retval.length ();
127 
128  if (len > 0)
129  {
130  while (len)
131  {
132  c = retval[len-1];
133 
134  if (c == ' ' || c == '\t')
135  len--;
136  else
137  {
138  retval.resize (len);
139  break;
140  }
141  }
142  }
143 
144  return retval;
145 }
146 
147 // Extract one value (scalar, matrix, string, etc.) from stream IS and
148 // place it in TC, returning the name of the variable. If the value
149 // is tagged as global in the file, return TRUE in GLOBAL.
150 //
151 // Each type supplies its own function to load the data, and so this
152 // function is extensible.
153 //
154 // FILENAME is used for error messages.
155 //
156 // The data is expected to be in the following format:
157 //
158 // The input file must have a header followed by some data.
159 //
160 // All lines in the header must begin with a '#' character.
161 //
162 // The header must contain a list of keyword and value pairs with the
163 // keyword and value separated by a colon.
164 //
165 // Keywords must appear in the following order:
166 //
167 // # name: <name>
168 // # type: <type>
169 // # <info>
170 //
171 // Where, for the built in types are:
172 //
173 // <name> : a valid identifier
174 //
175 // <type> : <typename>
176 // | global <typename>
177 //
178 // <typename> : scalar
179 // | complex scalar
180 // | matrix
181 // | complex matrix
182 // | bool
183 // | bool matrix
184 // | string
185 // | range
186 //
187 // <info> : <matrix info>
188 // | <string info>
189 //
190 // <matrix info> : # rows: <integer>
191 // : # columns: <integer>
192 //
193 // <string info> : # elements: <integer>
194 // : # length: <integer> (once before each string)
195 //
196 // For backward compatibility the type "string array" is treated as a
197 // "string" type. Also "string" can have a single element with no elements
198 // line such that
199 //
200 // <string info> : # length: <integer>
201 //
202 // Formatted ASCII data follows the header.
203 //
204 // Example:
205 //
206 // # name: foo
207 // # type: matrix
208 // # rows: 2
209 // # columns: 2
210 // 2 4
211 // 1 3
212 //
213 // Example:
214 //
215 // # name: foo
216 // # type: string
217 // # elements: 5
218 // # length: 4
219 // this
220 // # length: 2
221 // is
222 // # length: 1
223 // a
224 // # length: 6
225 // string
226 // # length: 5
227 // array
228 //
229 // FIXME: This format is fairly rigid, and doesn't allow for arbitrary comments.
230 // Someone should fix that. It does allow arbitrary types however.
231 
232 // Ugh. The signature of the compare method is not standard in older
233 // versions of the GNU libstdc++. Do this instead:
234 
235 #define SUBSTRING_COMPARE_EQ(s, pos, n, t) (s.substr (pos, n) == t)
236 
238 read_text_data (std::istream& is, const std::string& filename, bool& global,
239  octave_value& tc, octave_idx_type count)
240 {
241  // Read name for this entry or break on EOF.
242 
243  std::string name = extract_keyword (is, "name");
244 
245  if (name.empty ())
246  {
247  if (count == 0)
248  error ("load: empty name keyword or no data found in file '%s'",
249  filename.c_str ());
250 
251  return "";
252  }
253 
254  if (! (name == ".nargin." || name == ".nargout."
256  error ("load: invalid identifier '%s' found in file '%s'",
257  name.c_str (), filename.c_str ());
258 
259  // Look for type keyword.
260 
261  std::string tag = extract_keyword (is, "type");
262 
263  if (tag.empty ())
264  error ("load: failed to extract keyword specifying value type");
265 
266  std::string typ;
267  size_t pos = tag.rfind (' ');
268 
269  if (pos != std::string::npos)
270  {
271  global = SUBSTRING_COMPARE_EQ (tag, 0, 6, "global");
272 
273  typ = (global ? tag.substr (7) : tag);
274  }
275  else
276  typ = tag;
277 
278  // Special case for backward compatibility. A small bit of cruft
279  if (SUBSTRING_COMPARE_EQ (typ, 0, 12, "string array"))
280  tc = charMatrix ();
281  else
282  {
283  octave::type_info& type_info
284  = octave::__get_type_info__ ("read_text_data");
285 
286  tc = type_info.lookup_type (typ);
287  }
288 
289  if (! tc.load_ascii (is))
290  error ("load: trouble reading ascii file '%s'", filename.c_str ());
291 
292  return name;
293 }
294 
295 // Save the data from TC along with the corresponding NAME, and global
296 // flag MARK_AS_GLOBAL on stream OS in the plain text format described
297 // above for load_text_data. If NAME is empty, the name: line is not
298 // generated. PRECISION specifies the number of decimal digits to print.
299 //
300 // Assumes ranges and strings cannot contain Inf or NaN values.
301 //
302 // Returns 1 for success and 0 for failure.
303 
304 // FIXME: should probably write the help string here too.
305 
306 bool
307 save_text_data (std::ostream& os, const octave_value& val_arg,
308  const std::string& name, bool mark_global,
309  int precision)
310 {
311  bool success = true;
312 
313  if (! name.empty ())
314  os << "# name: " << name << "\n";
315 
316  octave_value val = val_arg;
317 
318  if (mark_global)
319  os << "# type: global " << val.type_name () << "\n";
320  else
321  os << "# type: " << val.type_name () << "\n";
322 
323  if (! precision)
324  precision = Vsave_precision;
325 
326  long old_precision = os.precision ();
327  os.precision (precision);
328 
329  success = val.save_ascii (os);
330 
331  // Insert an extra pair of newline characters after the data so that
332  // multiple data elements may be handled separately by gnuplot (see
333  // the description of the index qualifier for the plot command in the
334  // gnuplot documentation).
335  os << "\n\n";
336 
337  os.precision (old_precision);
338 
339  return (os && success);
340 }
341 
342 bool
344  const std::string& name)
345 {
346  return save_text_data (os, t, name, false, 6);
347 }
348 
349 // Maybe this should be a static function in tree-plot.cc?
350 
351 // If TC is matrix, save it on stream OS in a format useful for
352 // making a 3-D plot with gnuplot. If PARAMETRIC is TRUE,
353 // assume a parametric 3-D plot will be generated.
354 
355 bool
356 save_three_d (std::ostream& os, const octave_value& tc, bool parametric)
357 {
358  bool fail = false;
359 
360  octave_idx_type nr = tc.rows ();
361  octave_idx_type nc = tc.columns ();
362 
363  if (! tc.is_real_matrix ())
364  error ("for now, I can only save real matrices in 3-D format");
365 
366  os << "# 3-D data...\n"
367  << "# type: matrix\n"
368  << "# total rows: " << nr << "\n"
369  << "# total columns: " << nc << "\n";
370 
371  long old_precision = os.precision ();
372  os.precision (6);
373 
374  if (parametric)
375  {
376  octave_idx_type extras = nc % 3;
377  if (extras)
378  warning ("ignoring last %d columns", extras);
379 
380  Matrix tmp = tc.matrix_value ();
381  nr = tmp.rows ();
382 
383  for (octave_idx_type i = 0; i < nc-extras; i += 3)
384  {
385  os << tmp.extract (0, i, nr-1, i+2);
386  if (i+3 < nc-extras)
387  os << "\n";
388  }
389  }
390  else
391  {
392  Matrix tmp = tc.matrix_value ();
393  nr = tmp.rows ();
394 
395  for (octave_idx_type i = 0; i < nc; i++)
396  {
397  os << tmp.extract (0, i, nr-1, i);
398  if (i+1 < nc)
399  os << "\n";
400  }
401  }
402 
403  os.precision (old_precision);
404 
405  return (os && ! fail);
406 }
407 
408 DEFUN (save_precision, args, nargout,
409  doc: /* -*- texinfo -*-
410 @deftypefn {} {@var{val} =} save_precision ()
411 @deftypefnx {} {@var{old_val} =} save_precision (@var{new_val})
412 @deftypefnx {} {} save_precision (@var{new_val}, "local")
413 Query or set the internal variable that specifies the number of digits to
414 keep when saving data in text format.
415 
416 The default value is 17 which is the minimum necessary for the lossless saving
417 and restoring of IEEE-754 double values; For IEEE-754 single values the minimum
418 value is 9. If file size is a concern, it is probably better to choose a
419 binary format for saving data rather than to reduce the precision of the saved
420 values.
421 
422 When called from inside a function with the @qcode{"local"} option, the
423 variable is changed locally for the function and any subroutines it calls.
424 The original variable value is restored when exiting the function.
425 
426 @seealso{save_default_options}
427 @end deftypefn */)
428 {
429  return SET_INTERNAL_VARIABLE_WITH_LIMITS (save_precision, -1,
431 }
std::string read_until_newline(std::istream &is, bool keep_newline)
#define SET_INTERNAL_VARIABLE_WITH_LIMITS(NM, MINVAL, MAXVAL)
Definition: variables.h:115
octave_value lookup_type(const std::string &nm)
Definition: ov-typeinfo.cc:476
bool save_text_data_for_plotting(std::ostream &os, const octave_value &t, const std::string &name)
Definition: ls-oct-text.cc:343
identity matrix If supplied two scalar respectively For allows like xample val
Definition: data.cc:4986
bool is_real_matrix(void) const
Definition: ov.h:553
void skip_until_newline(std::istream &is, bool keep_newline)
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:53
void error(const char *fmt,...)
Definition: error.cc:578
std::string filename
Definition: urlwrite.cc:121
OCTAVE_EXPORT octave_value_list return the number of command line arguments passed to Octave If called with the optional argument the function t
Definition: ov-usr-fcn.cc:997
static int Vsave_precision
Definition: ls-oct-text.cc:70
nd example oindent opens the file binary numeric values will be read assuming they are stored in IEEE format with the least significant bit and then converted to the native representation Opening a file that is already open simply opens it again and returns a separate file id It is not an error to open a file several though writing to the same file through several different file ids may produce unexpected results The possible values of text mode reading and writing automatically converts linefeeds to the appropriate line end character for the you may append a you must also open the file in binary mode The parameter conversions are currently only supported for and permissions will be set to and then everything is written in a single operation This is very efficient and improves performance c
Definition: file-io.cc:587
#define SUBSTRING_COMPARE_EQ(s, pos, n, t)
Definition: ls-oct-text.cc:235
octave_value resize(const dim_vector &dv, bool fill=false) const
Definition: ov.h:511
nd deftypefn *std::string name
Definition: sysdep.cc:647
OCTAVE_EXPORT octave_value_list return the number of command line arguments passed to Octave If called with the optional argument the function xample nargout(@histc)
Definition: ov-usr-fcn.cc:997
type_info & __get_type_info__(const std::string &who)
bool valid_identifier(const char *s)
Definition: utils.cc:74
std::string extract_keyword(std::istream &is, const char *keyword, const bool next_only)
Definition: ls-oct-text.cc:82
octave_idx_type columns(void) const
Definition: ov.h:474
octave_idx_type rows(void) const
Definition: ov.h:472
double tmp
Definition: data.cc:6252
octave_value retval
Definition: data.cc:6246
Definition: dMatrix.h:36
T::size_type strlen(const typename T::value_type *str)
Definition: oct-string.cc:75
void warning(const char *fmt,...)
Definition: error.cc:801
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:227
#define CELL_ELT_TAG
Definition: ls-oct-text.h:39
bool load_ascii(std::istream &is)
Definition: ov.h:1336
std::string read_text_data(std::istream &is, const std::string &filename, bool &global, octave_value &tc, octave_idx_type count)
Definition: ls-oct-text.cc:238
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:307
for i
Definition: data.cc:5264
bool save_three_d(std::ostream &os, const octave_value &tc, bool parametric)
Definition: ls-oct-text.cc:356
octave_idx_type length(void) const
write the output to stdout if nargout is
Definition: load-save.cc:1612
If this string is the system will ring the terminal sometimes it is useful to be able to print the original representation of the string
Definition: utils.cc:888
nd group nd example For each display the value
Definition: sysdep.cc:866
octave::stream os
Definition: file-io.cc:627
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:834