ls-oct-ascii.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 1996-2012 John W. Eaton
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
00020 
00021 */
00022 
00023 // Author: John W. Eaton.
00024 
00025 #ifdef HAVE_CONFIG_H
00026 #include <config.h>
00027 #endif
00028 
00029 #include <cstring>
00030 #include <cctype>
00031 
00032 #include <fstream>
00033 #include <iomanip>
00034 #include <iostream>
00035 #include <sstream>
00036 #include <string>
00037 
00038 #include "byte-swap.h"
00039 #include "data-conv.h"
00040 #include "file-ops.h"
00041 #include "glob-match.h"
00042 #include "lo-mappers.h"
00043 #include "mach-info.h"
00044 #include "oct-env.h"
00045 #include "oct-time.h"
00046 #include "quit.h"
00047 #include "str-vec.h"
00048 
00049 #include "Cell.h"
00050 #include "defun.h"
00051 #include "error.h"
00052 #include "gripes.h"
00053 #include "load-save.h"
00054 #include "ls-ascii-helper.h"
00055 #include "ls-oct-ascii.h"
00056 #include "oct-obj.h"
00057 #include "oct-map.h"
00058 #include "ov-cell.h"
00059 #include "pager.h"
00060 #include "pt-exp.h"
00061 #include "unwind-prot.h"
00062 #include "utils.h"
00063 #include "variables.h"
00064 #include "version.h"
00065 #include "dMatrix.h"
00066 
00067 // The number of decimal digits to use when writing ascii data.
00068 static int Vsave_precision = 16;
00069 
00070 // Functions for reading ascii data.
00071 
00072 // Extract a KEYWORD and its value from stream IS, returning the
00073 // associated value in a new string.
00074 //
00075 // Input should look something like:
00076 //
00077 //  [%#][ \t]*keyword[ \t]*:[ \t]*string-value[ \t]*\n
00078 
00079 std::string
00080 extract_keyword (std::istream& is, const char *keyword, const bool next_only)
00081 {
00082   std::string retval;
00083 
00084   int ch = is.peek ();
00085   if (next_only && ch != '%' && ch != '#')
00086     return retval;
00087 
00088   char c;
00089   while (is.get (c))
00090     {
00091       if (c == '%' || c == '#')
00092         {
00093           std::ostringstream buf;
00094 
00095           while (is.get (c) && (c == ' ' || c == '\t' || c == '%' || c == '#'))
00096             ; // Skip whitespace and comment characters.
00097 
00098           if (isalpha (c))
00099             buf << c;
00100 
00101           while (is.get (c) && isalpha (c))
00102             buf << c;
00103 
00104           std::string tmp = buf.str ();
00105           bool match = (tmp.compare (0, strlen (keyword), keyword) == 0);
00106 
00107           if (match)
00108             {
00109               std::ostringstream value;
00110               while (is.get (c) && (c == ' ' || c == '\t' || c == ':'))
00111                 ; // Skip whitespace and the colon.
00112 
00113               is.putback(c);
00114               retval = read_until_newline (is, false);
00115               break;
00116             }
00117           else if (next_only)
00118             break;
00119           else
00120             skip_until_newline (is, false);
00121         }
00122     }
00123 
00124   int len = retval.length ();
00125 
00126   if (len > 0)
00127     {
00128       while (len)
00129         {
00130           c = retval[len-1];
00131 
00132           if (c == ' ' || c == '\t')
00133             len--;
00134           else
00135             {
00136               retval.resize (len);
00137               break;
00138             }
00139         }
00140     }
00141 
00142   return retval;
00143 }
00144 
00145 // Extract one value (scalar, matrix, string, etc.) from stream IS and
00146 // place it in TC, returning the name of the variable.  If the value
00147 // is tagged as global in the file, return TRUE in GLOBAL.
00148 //
00149 // Each type supplies its own function to load the data, and so this
00150 // function is extensible.
00151 //
00152 // FILENAME is used for error messages.
00153 //
00154 // The data is expected to be in the following format:
00155 //
00156 // The input file must have a header followed by some data.
00157 //
00158 // All lines in the header must begin with a '#' character.
00159 //
00160 // The header must contain a list of keyword and value pairs with the
00161 // keyword and value separated by a colon.
00162 //
00163 // Keywords must appear in the following order:
00164 //
00165 // # name: <name>
00166 // # type: <type>
00167 // # <info>
00168 //
00169 // Where, for the built in types are:
00170 //
00171 //  <name> : a valid identifier
00172 //
00173 //  <type> : <typename>
00174 //         | global <typename>
00175 //
00176 //  <typename> : scalar
00177 //             | complex scalar
00178 //             | matrix
00179 //             | complex matrix
00180 //             | bool
00181 //             | bool matrix
00182 //             | string
00183 //             | range
00184 //
00185 //  <info> : <matrix info>
00186 //         | <string info>
00187 //
00188 //  <matrix info> : # rows: <integer>
00189 //                : # columns: <integer>
00190 //
00191 //  <string info> : # elements: <integer>
00192 //                : # length: <integer> (once before each string)
00193 //
00194 //  For backward compatibility the type "string array" is treated as a
00195 // "string" type. Also "string" can have a single element with no elements
00196 // line such that
00197 //
00198 //  <string info> : # length: <integer>
00199 //
00200 // Formatted ASCII data follows the header.
00201 //
00202 // Example:
00203 //
00204 //  # name: foo
00205 //  # type: matrix
00206 //  # rows: 2
00207 //  # columns: 2
00208 //    2  4
00209 //    1  3
00210 //
00211 // Example:
00212 //
00213 //  # name: foo
00214 //  # type: string
00215 //  # elements: 5
00216 //  # length: 4
00217 //  this
00218 //  # length: 2
00219 //  is
00220 //  # length: 1
00221 //  a
00222 //  # length: 6
00223 //  string
00224 //  # length: 5
00225 //  array
00226 //
00227 // FIXME -- this format is fairly rigid, and doesn't allow for
00228 // arbitrary comments.  Someone should fix that. It does allow arbitrary
00229 // types however.
00230 
00231 // Ugh.  The signature of the compare method is not standard in older
00232 // versions of the GNU libstdc++.  Do this instead:
00233 
00234 #define SUBSTRING_COMPARE_EQ(s, pos, n, t) (s.substr (pos, n) == t)
00235 
00236 std::string
00237 read_ascii_data (std::istream& is, const std::string& filename, bool& global,
00238                  octave_value& tc, octave_idx_type count)
00239 {
00240   // Read name for this entry or break on EOF.
00241 
00242   std::string name = extract_keyword (is, "name");
00243 
00244   if (name.empty ())
00245     {
00246       if (count == 0)
00247         error ("load: empty name keyword or no data found in file '%s'",
00248                filename.c_str ());
00249 
00250       return std::string ();
00251     }
00252 
00253   if (! (name == ".nargin." || name == ".nargout."
00254          || name == CELL_ELT_TAG || valid_identifier (name)))
00255     {
00256       error ("load: bogus identifier '%s' found in file '%s'",
00257              name.c_str (), filename.c_str ());
00258       return std::string ();
00259     }
00260 
00261   // Look for type keyword.
00262 
00263   std::string tag = extract_keyword (is, "type");
00264 
00265   if (! tag.empty ())
00266     {
00267       std::string typ;
00268       size_t pos = tag.rfind (' ');
00269 
00270       if (pos != std::string::npos)
00271         {
00272           global = SUBSTRING_COMPARE_EQ (tag, 0, 6, "global");
00273 
00274           typ = global ? tag.substr (7) : tag;
00275         }
00276       else
00277         typ = tag;
00278 
00279       // Special case for backward compatiablity. A small bit of cruft
00280       if (SUBSTRING_COMPARE_EQ (typ, 0, 12, "string array"))
00281         tc = charMatrix ();
00282       else
00283         tc = octave_value_typeinfo::lookup_type (typ);
00284 
00285       if (! tc.load_ascii (is))
00286         error ("load: trouble reading ascii file '%s'", filename.c_str ());
00287     }
00288   else
00289     error ("load: failed to extract keyword specifying value type");
00290 
00291   if (error_state)
00292     {
00293       error ("load: reading file %s", filename.c_str ());
00294       return std::string ();
00295     }
00296 
00297   return name;
00298 }
00299 
00300 // Save the data from TC along with the corresponding NAME, and global
00301 // flag MARK_AS_GLOBAL on stream OS in the plain text format described
00302 // above for load_ascii_data.  If NAME is empty, the name: line is not
00303 // generated.  PRECISION specifies the number of decimal digits to print.
00304 //
00305 // Assumes ranges and strings cannot contain Inf or NaN values.
00306 //
00307 // Returns 1 for success and 0 for failure.
00308 
00309 // FIXME -- should probably write the help string here too.
00310 
00311 bool
00312 save_ascii_data (std::ostream& os, const octave_value& val_arg,
00313                  const std::string& name, bool mark_as_global,
00314                  int precision)
00315 {
00316   bool success = true;
00317 
00318   if (! name.empty ())
00319     os << "# name: " << name << "\n";
00320 
00321   octave_value val = val_arg;
00322 
00323   if (mark_as_global)
00324     os << "# type: global " << val.type_name () << "\n";
00325   else
00326     os << "# type: " << val.type_name() << "\n";
00327 
00328   if (! precision)
00329     precision = Vsave_precision;
00330 
00331   long old_precision = os.precision ();
00332   os.precision (precision);
00333 
00334   success = val.save_ascii (os);
00335 
00336   // Insert an extra pair of newline characters after the data so that
00337   // multiple data elements may be handled separately by gnuplot (see
00338   // the description of the index qualifier for the plot command in the
00339   // gnuplot documentation).
00340   os << "\n\n";
00341 
00342   os.precision (old_precision);
00343 
00344   return (os && success);
00345 }
00346 
00347 bool
00348 save_ascii_data_for_plotting (std::ostream& os, const octave_value& t,
00349                               const std::string& name)
00350 {
00351   return save_ascii_data (os, t, name, false, 6);
00352 }
00353 
00354 // Maybe this should be a static function in tree-plot.cc?
00355 
00356 // If TC is matrix, save it on stream OS in a format useful for
00357 // making a 3-dimensional plot with gnuplot.  If PARAMETRIC is
00358 // TRUE, assume a parametric 3-dimensional plot will be generated.
00359 
00360 bool
00361 save_three_d (std::ostream& os, const octave_value& tc, bool parametric)
00362 {
00363   bool fail = false;
00364 
00365   octave_idx_type nr = tc.rows ();
00366   octave_idx_type nc = tc.columns ();
00367 
00368   if (tc.is_real_matrix ())
00369     {
00370       os << "# 3D data...\n"
00371          << "# type: matrix\n"
00372          << "# total rows: " << nr << "\n"
00373          << "# total columns: " << nc << "\n";
00374 
00375       long old_precision = os.precision ();
00376       os.precision (6);
00377 
00378       if (parametric)
00379         {
00380           octave_idx_type extras = nc % 3;
00381           if (extras)
00382             warning ("ignoring last %d columns", extras);
00383 
00384           Matrix tmp = tc.matrix_value ();
00385           nr = tmp.rows ();
00386 
00387           for (octave_idx_type i = 0; i < nc-extras; i += 3)
00388             {
00389               os << tmp.extract (0, i, nr-1, i+2);
00390               if (i+3 < nc-extras)
00391                 os << "\n";
00392             }
00393         }
00394       else
00395         {
00396           Matrix tmp = tc.matrix_value ();
00397           nr = tmp.rows ();
00398 
00399           for (octave_idx_type i = 0; i < nc; i++)
00400             {
00401               os << tmp.extract (0, i, nr-1, i);
00402               if (i+1 < nc)
00403                 os << "\n";
00404             }
00405         }
00406 
00407       os.precision (old_precision);
00408     }
00409   else
00410     {
00411       ::error ("for now, I can only save real matrices in 3D format");
00412       fail = true;
00413     }
00414 
00415   return (os && ! fail);
00416 }
00417 
00418 DEFUN (save_precision, args, nargout,
00419     "-*- texinfo -*-\n\
00420 @deftypefn  {Built-in Function} {@var{val} =} save_precision ()\n\
00421 @deftypefnx {Built-in Function} {@var{old_val} =} save_precision (@var{new_val})\n\
00422 @deftypefnx {Built-in Function} {} save_precision (@var{new_val}, \"local\")\n\
00423 Query or set the internal variable that specifies the number of\n\
00424 digits to keep when saving data in text format.\n\
00425 \n\
00426 When called from inside a function with the \"local\" option, the variable is\n\
00427 changed locally for the function and any subroutines it calls.  The original\n\
00428 variable value is restored when exiting the function.\n\
00429 @end deftypefn")
00430 {
00431   return SET_INTERNAL_VARIABLE_WITH_LIMITS (save_precision, -1, INT_MAX);
00432 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines