GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
ls-oct-text.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 1996-2025 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
257 octave::skip_preceeding_newline (is);
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 = octave::__get_interpreter__ ();
274
275 octave_value_list tmp = interp.feval ("inline", args, 1);
276
277 if (tmp.length () > 0)
278 return tmp(0);
279 }
280 }
281
282 error ("load: trouble reading ascii file '%s'", filename.c_str ());
283}
284
285std::string
286read_text_data (std::istream& is, const std::string& filename, bool& global,
287 octave_value& tc, octave_idx_type count,
288 const bool do_name_validation)
289{
290 // Read name for this entry or break on EOF.
291
292 std::string name = extract_keyword (is, "name");
293
294 if (name.empty ())
295 {
296 if (count == 0)
297 error ("load: empty name keyword or no data found in file '%s'",
298 filename.c_str ());
299
300 return "";
301 }
302
303 if (name != CELL_ELT_TAG
304 && do_name_validation && ! octave::valid_identifier (name))
305 error ("load: invalid identifier '%s' found in file '%s'",
306 name.c_str (), filename.c_str ());
307
308 // Look for type keyword.
309
310 std::string tag = extract_keyword (is, "type");
311
312 if (tag.empty ())
313 error ("load: failed to extract keyword specifying value type");
314
315 std::string typ;
316 std::size_t pos = tag.rfind (' ');
317
318 if (pos != std::string::npos)
319 {
320 global = SUBSTRING_COMPARE_EQ (tag, 0, 6, "global");
321
322 typ = (global ? tag.substr (7) : tag);
323 }
324 else
325 typ = tag;
326
327 // Special case for backward compatibility. A small bit of cruft
328 if (SUBSTRING_COMPARE_EQ (typ, 0, 12, "string array"))
329 tc = charMatrix ();
330 else if (SUBSTRING_COMPARE_EQ (typ, 0, 15, "inline function"))
331 {
332 // Special case for loading old octave_inline_fcn objects.
333 tc = load_inline_fcn (is, filename);
334 return name;
335 }
336 else
337 {
338 octave::type_info& type_info = octave::__get_type_info__ ();
339
340 tc = type_info.lookup_type (typ);
341 }
342
343 if (! tc.load_ascii (is))
344 error ("load: trouble reading ascii file '%s'", filename.c_str ());
345
346 return name;
347}
348
349// Save the data from TC along with the corresponding NAME, and global
350// flag MARK_AS_GLOBAL on stream OS in the plain text format described
351// above for load_text_data. If NAME is empty, the name: line is not
352// generated. PRECISION specifies the number of decimal digits to print.
353//
354// Assumes ranges and strings cannot contain Inf or NaN values.
355//
356// Returns 1 for success and 0 for failure.
357
358// FIXME: should probably write the help string here too.
359
360bool
361save_text_data (std::ostream& os, const octave_value& val_arg,
362 const std::string& name, bool mark_global,
363 int precision)
364{
365 if (! name.empty ())
366 os << "# name: " << name << "\n";
367
368 octave_value val = val_arg;
369
370 if (mark_global)
371 os << "# type: global " << val.type_name () << "\n";
372 else
373 os << "# type: " << val.type_name () << "\n";
374
375 if (! precision)
376 precision = Vsave_precision;
377
378 long old_precision = os.precision ();
379 os.precision (precision);
380
381 bool success = val.save_ascii (os);
382
383 // Insert an extra pair of newline characters after the data so that
384 // multiple data elements may be handled separately by gnuplot (see
385 // the description of the index qualifier for the plot command in the
386 // gnuplot documentation).
387 os << "\n\n";
388
389 os.precision (old_precision);
390
391 return (os && success);
392}
393
394bool
395save_text_data_for_plotting (std::ostream& os, const octave_value& t,
396 const std::string& name)
397{
398 return save_text_data (os, t, name, false, 6);
399}
400
401// Maybe this should be a static function in tree-plot.cc?
402
403// If TC is matrix, save it on stream OS in a format useful for
404// making a 3-D plot with gnuplot. If PARAMETRIC is TRUE,
405// assume a parametric 3-D plot will be generated.
406
407bool
408save_three_d (std::ostream& os, const octave_value& tc, bool parametric)
409{
410 octave_idx_type nr = tc.rows ();
411 octave_idx_type nc = tc.columns ();
412
413 if (! tc.is_real_matrix ())
414 error ("for now, I can only save real matrices in 3-D format");
415
416 os << "# 3-D data...\n"
417 << "# type: matrix\n"
418 << "# total rows: " << nr << "\n"
419 << "# total columns: " << nc << "\n";
420
421 long old_precision = os.precision ();
422 os.precision (6);
423
424 if (parametric)
425 {
426 octave_idx_type extras = nc % 3;
427 if (extras)
428 warning ("ignoring last %" OCTAVE_IDX_TYPE_FORMAT " columns", extras);
429
430 Matrix tmp = tc.matrix_value ();
431 nr = tmp.rows ();
432
433 for (octave_idx_type i = 0; i < nc-extras; i += 3)
434 {
435 os << tmp.extract (0, i, nr-1, i+2);
436 if (i+3 < nc-extras)
437 os << "\n";
438 }
439 }
440 else
441 {
442 Matrix tmp = tc.matrix_value ();
443 nr = tmp.rows ();
444
445 for (octave_idx_type i = 0; i < nc; i++)
446 {
447 os << tmp.extract (0, i, nr-1, i);
448 if (i+1 < nc)
449 os << "\n";
450 }
451 }
452
453 os.precision (old_precision);
454
455 return (static_cast<bool> (os));
456}
457
459
460DEFUN (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 {} {@var{old_val} =} save_precision (@var{new_val}, "local")
465Query or set the internal variable that specifies the number of digits to
466keep when saving data in text format.
467
468The default value is 17 which is the minimum necessary for the lossless saving
469and restoring of IEEE-754 double values; For IEEE-754 single values the minimum
470value is 9. If file size is a concern, it is probably better to choose a
471binary format for saving data rather than to reduce the precision of the saved
472values.
473
474When called from inside a function with the @qcode{"local"} option, the
475variable is changed locally for the function and any subroutines it calls.
476The original variable value is restored when exiting the function.
477
478@seealso{save_default_options}
479@end deftypefn */)
480{
481 return set_internal_variable (Vsave_precision, args, nargout,
482 "save_precision", -1,
483 std::numeric_limits<int>::max ());
484}
485
486OCTAVE_END_NAMESPACE(octave)
octave_idx_type rows() const
Definition Array.h:463
Matrix extract(octave_idx_type r1, octave_idx_type c1, octave_idx_type r2, octave_idx_type c2) const
Definition dMatrix.cc:398
octave_idx_type length() const
Definition ovl.h:111
octave_idx_type rows() const
Definition ov.h:545
bool save_ascii(std::ostream &os)
Definition ov.h:1381
bool load_ascii(std::istream &is)
bool is_real_matrix() const
Definition ov.h:613
std::string type_name() const
Definition ov.h:1360
octave_idx_type columns() const
Definition ov.h:547
Matrix matrix_value(bool frc_str_conv=false) const
Definition ov.h:859
octave_value lookup_type(const std::string &nm)
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
#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:1078
void error(const char *fmt,...)
Definition error.cc:1003
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)
bool save_text_data_for_plotting(std::ostream &os, const octave_value &t, const std::string &name)
std::string extract_keyword(std::istream &is, const char *keyword, const bool next_only)
bool save_text_data(std::ostream &os, const octave_value &val_arg, const std::string &name, bool mark_global, int precision)
bool save_three_d(std::ostream &os, const octave_value &tc, bool parametric)
#define SUBSTRING_COMPARE_EQ(s, pos, n, t)
#define CELL_ELT_TAG
Definition ls-oct-text.h:42
T::size_type strlen(const typename T::value_type *str)
Definition oct-string.cc:88
octave_value set_internal_variable(bool &var, const octave_value_list &args, int nargout, const char *nm)
Definition variables.cc:583
F77_RET_T len
Definition xerbla.cc:61