GNU Octave 7.1.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-2022 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.
72static 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
83std::string
84extract_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 = octave::read_until_newline (is, false);
119 break;
120 }
121 else if (next_only)
122 break;
123 else
124 octave::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
239static octave_value
240load_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 = octave::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
286std::string
287read_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 std::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
362bool
363save_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
396bool
397save_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
409bool
410save_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
460OCTAVE_NAMESPACE_BEGIN
461
462DEFUN (save_precision, args, nargout,
463 doc: /* -*- texinfo -*-
464@deftypefn {} {@var{val} =} save_precision ()
465@deftypefnx {} {@var{old_val} =} save_precision (@var{new_val})
466@deftypefnx {} {} save_precision (@var{new_val}, "local")
467Query or set the internal variable that specifies the number of digits to
468keep when saving data in text format.
469
470The default value is 17 which is the minimum necessary for the lossless saving
471and restoring of IEEE-754 double values; For IEEE-754 single values the minimum
472value is 9. If file size is a concern, it is probably better to choose a
473binary format for saving data rather than to reduce the precision of the saved
474values.
475
476When called from inside a function with the @qcode{"local"} option, the
477variable is changed locally for the function and any subroutines it calls.
478The original variable value is restored when exiting the function.
479
480@seealso{save_default_options}
481@end deftypefn */)
482{
483 return set_internal_variable (Vsave_precision, args, nargout,
484 "save_precision", -1,
486}
487
488OCTAVE_NAMESPACE_END
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
octave_idx_type rows(void) const
Definition: Array.h:449
Definition: dMatrix.h:42
OCTAVE_API Matrix extract(octave_idx_type r1, octave_idx_type c1, octave_idx_type r2, octave_idx_type c2) const
Definition: dMatrix.cc:397
octave_idx_type length(void) const
Definition: ovl.h:113
OCTINTERP_API bool load_ascii(std::istream &is)
octave_idx_type rows(void) const
Definition: ov.h:590
bool save_ascii(std::ostream &os)
Definition: ov.h:1487
octave_idx_type columns(void) const
Definition: ov.h:592
bool is_real_matrix(void) const
Definition: ov.h:658
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:898
std::string type_name(void) const
Definition: ov.h:1449
octave_value lookup_type(const std::string &nm)
Definition: ov-typeinfo.cc:480
#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:1055
void error(const char *fmt,...)
Definition: error.cc:980
QString name
OCTAVE_NAMESPACE_BEGIN 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
interpreter & __get_interpreter__(const std::string &who)
type_info & __get_type_info__(const std::string &who)
T::size_type strlen(const typename T::value_type *str)
Definition: oct-string.cc:85
OCTAVE_NAMESPACE_BEGIN bool valid_identifier(const char *s)
Definition: utils.cc:77
octave_value set_internal_variable(bool &var, const octave_value_list &args, int nargout, const char *nm)
Definition: variables.cc:587
F77_RET_T len
Definition: xerbla.cc:61