Mercurial > octave
changeset 26110:b543cf12c63f
Move octave_str2double to liboctave (patch #9084).
* libinterp/corefcn/strfns.cc: Move "Fstr2double" from "str2double.cc".
* liboctave/util/oct-string.cc: Move functions from "str2double.cc".
* libinterp/util/oct-string.h: Add function "octave_str2double".
* libinterp/corefcn/str2double.cc: Delete file.
author | Markus Mützel <markus.muetzel@gmx.de> |
---|---|
date | Sun, 18 Nov 2018 15:44:36 +0100 |
parents | 31739d91ee49 |
children | 3e44ed9d50b6 |
files | libinterp/corefcn/module.mk libinterp/corefcn/str2double.cc libinterp/corefcn/strfns.cc liboctave/util/oct-string.cc liboctave/util/oct-string.h |
diffstat | 5 files changed, 393 insertions(+), 427 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/module.mk Wed Nov 21 10:35:43 2018 -0800 +++ b/libinterp/corefcn/module.mk Sun Nov 18 15:44:36 2018 +0100 @@ -232,7 +232,6 @@ %reldir%/sparse.cc \ %reldir%/spparms.cc \ %reldir%/sqrtm.cc \ - %reldir%/str2double.cc \ %reldir%/strfind.cc \ %reldir%/strfns.cc \ %reldir%/sub2ind.cc \
--- a/libinterp/corefcn/str2double.cc Wed Nov 21 10:35:43 2018 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,426 +0,0 @@ -/* - -Copyright (C) 2010-2018 Jaroslav Hajek -Copyright (C) 2010 VZLU Prague - -This file is part of Octave. - -Octave is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Octave is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Octave; see the file COPYING. If not, see -<https://www.gnu.org/licenses/>. - -*/ - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include <string> -#include <cctype> -#include <sstream> -#include <algorithm> - -#include "lo-ieee.h" - -#include "Cell.h" -#include "ov.h" -#include "defun.h" -#include "errwarn.h" -#include "utils.h" - -static inline bool -is_imag_unit (int c) -{ return c == 'i' || c == 'j'; } - -static double -single_num (std::istringstream& is) -{ - double num = 0.0; - - char c = is.peek (); - - // Skip spaces. - while (isspace (c)) - { - is.get (); - c = is.peek (); - } - - if (std::toupper (c) == 'I') - { - // It's infinity. - is.get (); - char c1 = is.get (); - char c2 = is.get (); - if (std::tolower (c1) == 'n' && std::tolower (c2) == 'f') - { - num = octave::numeric_limits<double>::Inf (); - is.peek (); // May set EOF bit. - } - else - is.setstate (std::ios::failbit); // indicate that read has failed. - } - else if (c == 'N') - { - // It's NA or NaN - is.get (); - char c1 = is.get (); - if (c1 == 'A') - { - num = octave_NA; - is.peek (); // May set EOF bit. - } - else - { - char c2 = is.get (); - if (c1 == 'a' && c2 == 'N') - { - num = octave::numeric_limits<double>::NaN (); - is.peek (); // May set EOF bit. - } - else - is.setstate (std::ios::failbit); // indicate that read has failed. - } - } - else - is >> num; - - return num; -} - -static std::istringstream& -extract_num (std::istringstream& is, double& num, bool& imag, bool& have_sign) -{ - have_sign = imag = false; - - char c = is.peek (); - - // Skip leading spaces. - while (isspace (c)) - { - is.get (); - c = is.peek (); - } - - bool negative = false; - - // Accept leading sign. - if (c == '+' || c == '-') - { - have_sign = true; - negative = c == '-'; - is.get (); - c = is.peek (); - } - - // Skip spaces after sign. - while (isspace (c)) - { - is.get (); - c = is.peek (); - } - - // Imaginary number (i*num or just i), or maybe 'inf'. - if (c == 'i') - { - // possible infinity. - is.get (); - c = is.peek (); - - if (is.eof ()) - { - // just 'i' and string is finished. Return immediately. - imag = true; - num = (negative ? -1.0 : 1.0); - return is; - } - else - { - if (std::tolower (c) != 'n') - imag = true; - is.unget (); - } - } - else if (c == 'j') - imag = true; - - // It's i*num or just i - if (imag) - { - is.get (); - c = is.peek (); - // Skip spaces after imaginary unit. - while (isspace (c)) - { - is.get (); - c = is.peek (); - } - - if (c == '*') - { - // Multiplier follows, we extract it as a number. - is.get (); - num = single_num (is); - if (is.good ()) - c = is.peek (); - } - else - num = 1.0; - } - else - { - // It's num, num*i, or numi. - num = single_num (is); - if (is.good ()) - { - c = is.peek (); - - // Skip spaces after number. - while (isspace (c)) - { - is.get (); - c = is.peek (); - } - - if (c == '*') - { - is.get (); - c = is.peek (); - - // Skip spaces after operator. - while (isspace (c)) - { - is.get (); - c = is.peek (); - } - - if (is_imag_unit (c)) - { - imag = true; - is.get (); - c = is.peek (); - } - else - is.setstate (std::ios::failbit); // indicate read has failed. - } - else if (is_imag_unit (c)) - { - imag = true; - is.get (); - c = is.peek (); - } - } - } - - if (is.good ()) - { - // Skip trailing spaces. - while (isspace (c)) - { - is.get (); - c = is.peek (); - } - } - - if (negative) - num = -num; - - return is; -} - -static inline void -set_component (Complex& c, double num, bool imag) -{ -#if defined (HAVE_CXX_COMPLEX_SETTERS) - if (imag) - c.imag (num); - else - c.real (num); -#elif defined (HAVE_CXX_COMPLEX_REFERENCE_ACCESSORS) - if (imag) - c.imag () = num; - else - c.real () = num; -#else - if (imag) - c = Complex (c.real (), num); - else - c = Complex (num, c.imag ()); -#endif -} - -static Complex -str2double1 (const std::string& str_arg) -{ - Complex val (0.0, 0.0); - - std::string str = str_arg; - - // FIXME: removing all commas doesn't allow actual parsing. - // Example: "1,23.45" is wrong, but passes Octave. - str.erase (std::remove (str.begin (), str.end(), ','), str.end ()); - std::istringstream is (str); - - double num; - bool i1, i2, s1, s2; - - if (is.eof ()) - val = octave::numeric_limits<double>::NaN (); - else if (! extract_num (is, num, i1, s1)) - val = octave::numeric_limits<double>::NaN (); - else - { - set_component (val, num, i1); - - if (! is.eof ()) - { - if (! extract_num (is, num, i2, s2) || i1 == i2 || ! s2) - val = octave::numeric_limits<double>::NaN (); - else - set_component (val, num, i2); - } - } - - return val; -} - -DEFUN (str2double, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {} str2double (@var{s}) -Convert a string to a real or complex number. - -The string must be in one of the following formats where a and b are real -numbers and the complex unit is @qcode{'i'} or @qcode{'j'}: - -@itemize -@item a + bi - -@item a + b*i - -@item a + i*b - -@item bi + a - -@item b*i + a - -@item i*b + a -@end itemize - -If present, a and/or b are of the form @nospell{[+-]d[,.]d[[eE][+-]d]} where -the brackets indicate optional arguments and @qcode{'d'} indicates zero or -more digits. The special input values @code{Inf}, @code{NaN}, and @code{NA} -are also accepted. - -@var{s} may be a character string, character matrix, or cell array. For -character arrays the conversion is repeated for every row, and a double or -complex array is returned. Empty rows in @var{s} are deleted and not -returned in the numeric array. For cell arrays each character string -element is processed and a double or complex array of the same dimensions as -@var{s} is returned. - -For unconvertible scalar or character string input @code{str2double} returns -a NaN@. Similarly, for character array input @code{str2double} returns a -NaN for any row of @var{s} that could not be converted. For a cell array, -@code{str2double} returns a NaN for any element of @var{s} for which -conversion fails. Note that numeric elements in a mixed string/numeric -cell array are not strings and the conversion will fail for these elements -and return NaN. - -@code{str2double} can replace @code{str2num}, and it avoids the security -risk of using @code{eval} on unknown data. -@seealso{str2num} -@end deftypefn */) -{ - if (args.length () != 1) - print_usage (); - - octave_value retval; - - if (args(0).is_string ()) - { - if (args(0).rows () == 0 || args(0).columns () == 0) - retval = Matrix (1, 1, octave::numeric_limits<double>::NaN ()); - else if (args(0).rows () == 1 && args(0).ndims () == 2) - retval = str2double1 (args(0).string_value ()); - else - { - const string_vector sv = args(0).string_vector_value (); - - retval = sv.map<Complex> (str2double1); - } - } - else if (args(0).iscell ()) - { - const Cell cell = args(0).cell_value (); - - ComplexNDArray output (cell.dims (), octave::numeric_limits<double>::NaN ()); - - for (octave_idx_type i = 0; i < cell.numel (); i++) - { - if (cell(i).is_string ()) - output(i) = str2double1 (cell(i).string_value ()); - } - retval = output; - } - else - retval = Matrix (1, 1, octave::numeric_limits<double>::NaN ()); - - return retval; -} - -/* -%!assert (str2double ("1"), 1) -%!assert (str2double ("-.1e-5"), -1e-6) -%!testif ; ! ismac () -%! assert (str2double (char ("1", "2 3", "4i")), [1; NaN; 4i]); -%!xtest <47413> -%! ## Same test code as above, but intended only for test statistics on Mac. -%! if (! ismac ()), return; endif -%! assert (str2double (char ("1", "2 3", "4i")), [1; NaN; 4i]); -%!assert (str2double ("1,222.5"), 1222.5) -%!assert (str2double ("i"), i) -%!assert (str2double ("2j"), 2i) -%!assert (str2double ("2 + j"), 2+j) -%!assert (str2double ("i*2 + 3"), 3+2i) -%!assert (str2double (".5*i + 3.5"), 3.5+0.5i) -%!assert (str2double ("1e-3 + i*.25"), 1e-3 + 0.25i) -%!assert (str2double (char ("2 + j","1.25e-3","-05")), [2+i; 1.25e-3; -5]) -%!assert (str2double ({"2 + j","1.25e-3","-05"}), [2+i, 1.25e-3, -5]) -%!assert (str2double (1), NaN) -%!assert (str2double ("1 2 3 4"), NaN) -%!assert (str2double ("Hello World"), NaN) -%!assert (str2double ("NaN"), NaN) -%!assert (str2double ("NA"), NA) -%!assert (str2double ("Inf"), Inf) -%!assert (str2double ("iNF"), Inf) -%!assert (str2double ("-Inf"), -Inf) -%!assert (str2double ("Inf*i"), complex (0, Inf)) -%!assert (str2double ("iNF*i"), complex (0, Inf)) -%!assert (str2double ("NaN + Inf*i"), complex (NaN, Inf)) -%!assert (str2double ("Inf - Inf*i"), complex (Inf, -Inf)) -%!assert (str2double ("-i*NaN - Inf"), complex (-Inf, -NaN)) -%!testif ; ! ismac () -%! assert (str2double ({"abc", "4i"}), [NaN + 0i, 4i]); -%!xtest <47413> -%! if (! ismac ()), return; endif -%! assert (str2double ({"abc", "4i"}), [NaN + 0i, 4i]); -%!testif ; ! ismac () -%! assert (str2double ({2, "4i"}), [NaN + 0i, 4i]) -%!xtest <47413> -%! if (! ismac ()), return; endif -%! assert (str2double ({2, "4i"}), [NaN + 0i, 4i]) -%!assert (str2double (zeros (3,1,2)), NaN) -%!assert (str2double (''), NaN) -%!assert (str2double ([]), NaN) -%!assert (str2double (char(zeros(3,0))), NaN) -*/
--- a/libinterp/corefcn/strfns.cc Wed Nov 21 10:35:43 2018 -0800 +++ b/libinterp/corefcn/strfns.cc Sun Nov 18 15:44:36 2018 +0100 @@ -739,6 +739,137 @@ %!assert <*54373> (strncmpi ("abc", "abC", 100)) */ +DEFUN (str2double, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {} str2double (@var{s}) +Convert a string to a real or complex number. + +The string must be in one of the following formats where a and b are real +numbers and the complex unit is @qcode{'i'} or @qcode{'j'}: + +@itemize +@item a + bi + +@item a + b*i + +@item a + i*b + +@item bi + a + +@item b*i + a + +@item i*b + a +@end itemize + +If present, a and/or b are of the form @nospell{[+-]d[,.]d[[eE][+-]d]} where +the brackets indicate optional arguments and @qcode{'d'} indicates zero or +more digits. The special input values @code{Inf}, @code{NaN}, and @code{NA} +are also accepted. + +@var{s} may be a character string, character matrix, or cell array. For +character arrays the conversion is repeated for every row, and a double or +complex array is returned. Empty rows in @var{s} are deleted and not +returned in the numeric array. For cell arrays each character string +element is processed and a double or complex array of the same dimensions as +@var{s} is returned. + +For unconvertible scalar or character string input @code{str2double} returns +a NaN@. Similarly, for character array input @code{str2double} returns a +NaN for any row of @var{s} that could not be converted. For a cell array, +@code{str2double} returns a NaN for any element of @var{s} for which +conversion fails. Note that numeric elements in a mixed string/numeric +cell array are not strings and the conversion will fail for these elements +and return NaN. + +@code{str2double} can replace @code{str2num}, and it avoids the security +risk of using @code{eval} on unknown data. +@seealso{str2num} +@end deftypefn */) +{ + if (args.length () != 1) + print_usage (); + + octave_value retval; + + if (args(0).is_string ()) + { + if (args(0).rows () == 0 || args(0).columns () == 0) + retval = Matrix (1, 1, octave::numeric_limits<double>::NaN ()); + else if (args(0).rows () == 1 && args(0).ndims () == 2) + retval = octave_str2double (args(0).string_value ()); + else + { + const string_vector sv = args(0).string_vector_value (); + + retval = sv.map<Complex> (octave_str2double); + } + } + else if (args(0).iscell ()) + { + const Cell cell = args(0).cell_value (); + + ComplexNDArray output (cell.dims (), octave::numeric_limits<double>::NaN ()); + + for (octave_idx_type i = 0; i < cell.numel (); i++) + { + if (cell(i).is_string ()) + output(i) = octave_str2double (cell(i).string_value ()); + } + retval = output; + } + else + retval = Matrix (1, 1, octave::numeric_limits<double>::NaN ()); + + return retval; +} + +/* +%!assert (str2double ("1"), 1) +%!assert (str2double ("-.1e-5"), -1e-6) +%!testif ; ! ismac () +%! assert (str2double (char ("1", "2 3", "4i")), [1; NaN; 4i]); +%!xtest <47413> +%! ## Same test code as above, but intended only for test statistics on Mac. +%! if (! ismac ()), return; endif +%! assert (str2double (char ("1", "2 3", "4i")), [1; NaN; 4i]); +%!assert (str2double ("1,222.5"), 1222.5) +%!assert (str2double ("i"), i) +%!assert (str2double ("2j"), 2i) +%!assert (str2double ("2 + j"), 2+j) +%!assert (str2double ("i*2 + 3"), 3+2i) +%!assert (str2double (".5*i + 3.5"), 3.5+0.5i) +%!assert (str2double ("1e-3 + i*.25"), 1e-3 + 0.25i) +%!assert (str2double (char ("2 + j","1.25e-3","-05")), [2+i; 1.25e-3; -5]) +%!assert (str2double ({"2 + j","1.25e-3","-05"}), [2+i, 1.25e-3, -5]) +%!assert (str2double (1), NaN) +%!assert (str2double ("1 2 3 4"), NaN) +%!assert (str2double ("Hello World"), NaN) +%!assert (str2double ("NaN"), NaN) +%!assert (str2double ("NA"), NA) +%!assert (str2double ("Inf"), Inf) +%!assert (str2double ("iNF"), Inf) +%!assert (str2double ("-Inf"), -Inf) +%!assert (str2double ("Inf*i"), complex (0, Inf)) +%!assert (str2double ("iNF*i"), complex (0, Inf)) +%!assert (str2double ("NaN + Inf*i"), complex (NaN, Inf)) +%!assert (str2double ("Inf - Inf*i"), complex (Inf, -Inf)) +%!assert (str2double ("-i*NaN - Inf"), complex (-Inf, -NaN)) +%!testif ; ! ismac () +%! assert (str2double ({"abc", "4i"}), [NaN + 0i, 4i]); +%!xtest <47413> +%! if (! ismac ()), return; endif +%! assert (str2double ({"abc", "4i"}), [NaN + 0i, 4i]); +%!testif ; ! ismac () +%! assert (str2double ({2, "4i"}), [NaN + 0i, 4i]) +%!xtest <47413> +%! if (! ismac ()), return; endif +%! assert (str2double ({2, "4i"}), [NaN + 0i, 4i]) +%!assert (str2double (zeros (3,1,2)), NaN) +%!assert (str2double (''), NaN) +%!assert (str2double ([]), NaN) +%!assert (str2double (char(zeros(3,0))), NaN) +*/ + DEFUN (__native2unicode__, args, , doc: /* -*- texinfo -*- @deftypefn {} {@var{utf8_str} =} __native2unicode__ (@var{native_bytes}, @var{codepage})
--- a/liboctave/util/oct-string.cc Wed Nov 21 10:35:43 2018 -0800 +++ b/liboctave/util/oct-string.cc Sun Nov 18 15:44:36 2018 +0100 @@ -25,6 +25,7 @@ #include "oct-string.h" +#include <algorithm> #include <cctype> #include <cstring> @@ -224,3 +225,259 @@ INSTANTIATE_OCTAVE_STRING(Array<char>) #undef INSTANTIATE_OCTAVE_STRING + +static inline bool +is_imag_unit (int c) +{ return c == 'i' || c == 'j'; } + +static double +single_num (std::istringstream& is) +{ + double num = 0.0; + + char c = is.peek (); + + // Skip spaces. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + + if (std::toupper (c) == 'I') + { + // It's infinity. + is.get (); + char c1 = is.get (); + char c2 = is.get (); + if (std::tolower (c1) == 'n' && std::tolower (c2) == 'f') + { + num = octave::numeric_limits<double>::Inf (); + is.peek (); // May set EOF bit. + } + else + is.setstate (std::ios::failbit); // indicate that read has failed. + } + else if (c == 'N') + { + // It's NA or NaN + is.get (); + char c1 = is.get (); + if (c1 == 'A') + { + num = octave_NA; + is.peek (); // May set EOF bit. + } + else + { + char c2 = is.get (); + if (c1 == 'a' && c2 == 'N') + { + num = octave::numeric_limits<double>::NaN (); + is.peek (); // May set EOF bit. + } + else + is.setstate (std::ios::failbit); // indicate that read has failed. + } + } + else + is >> num; + + return num; +} + +static std::istringstream& +extract_num (std::istringstream& is, double& num, bool& imag, bool& have_sign) +{ + have_sign = imag = false; + + char c = is.peek (); + + // Skip leading spaces. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + + bool negative = false; + + // Accept leading sign. + if (c == '+' || c == '-') + { + have_sign = true; + negative = c == '-'; + is.get (); + c = is.peek (); + } + + // Skip spaces after sign. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + + // Imaginary number (i*num or just i), or maybe 'inf'. + if (c == 'i') + { + // possible infinity. + is.get (); + c = is.peek (); + + if (is.eof ()) + { + // just 'i' and string is finished. Return immediately. + imag = true; + num = (negative ? -1.0 : 1.0); + return is; + } + else + { + if (std::tolower (c) != 'n') + imag = true; + is.unget (); + } + } + else if (c == 'j') + imag = true; + + // It's i*num or just i + if (imag) + { + is.get (); + c = is.peek (); + // Skip spaces after imaginary unit. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + + if (c == '*') + { + // Multiplier follows, we extract it as a number. + is.get (); + num = single_num (is); + if (is.good ()) + c = is.peek (); + } + else + num = 1.0; + } + else + { + // It's num, num*i, or numi. + num = single_num (is); + if (is.good ()) + { + c = is.peek (); + + // Skip spaces after number. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + + if (c == '*') + { + is.get (); + c = is.peek (); + + // Skip spaces after operator. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + + if (is_imag_unit (c)) + { + imag = true; + is.get (); + c = is.peek (); + } + else + is.setstate (std::ios::failbit); // indicate read has failed. + } + else if (is_imag_unit (c)) + { + imag = true; + is.get (); + c = is.peek (); + } + } + } + + if (is.good ()) + { + // Skip trailing spaces. + while (isspace (c)) + { + is.get (); + c = is.peek (); + } + } + + if (negative) + num = -num; + + return is; +} + +static inline void +set_component (Complex& c, double num, bool imag) +{ +#if defined (HAVE_CXX_COMPLEX_SETTERS) + if (imag) + c.imag (num); + else + c.real (num); +#elif defined (HAVE_CXX_COMPLEX_REFERENCE_ACCESSORS) + if (imag) + c.imag () = num; + else + c.real () = num; +#else + if (imag) + c = Complex (c.real (), num); + else + c = Complex (num, c.imag ()); +#endif +} + +Complex +octave_str2double (const std::string& str_arg) +{ + Complex val (0.0, 0.0); + + std::string str = str_arg; + + // FIXME: removing all commas doesn't allow actual parsing. + // Example: "1,23.45" is wrong, but passes Octave. + str.erase (std::remove (str.begin (), str.end(), ','), str.end ()); + std::istringstream is (str); + + double num; + bool i1, i2, s1, s2; + + if (is.eof ()) + val = octave::numeric_limits<double>::NaN (); + else if (! extract_num (is, num, i1, s1)) + val = octave::numeric_limits<double>::NaN (); + else + { + set_component (val, num, i1); + + if (! is.eof ()) + { + if (! extract_num (is, num, i2, s2) || i1 == i2 || ! s2) + val = octave::numeric_limits<double>::NaN (); + else + set_component (val, num, i2); + } + } + + return val; +}
--- a/liboctave/util/oct-string.h Wed Nov 21 10:35:43 2018 -0800 +++ b/liboctave/util/oct-string.h Sun Nov 18 15:44:36 2018 +0100 @@ -24,6 +24,8 @@ #include "octave-config.h" +#include "oct-cmplx.h" + namespace octave { //! Octave string utility functions. @@ -123,4 +125,7 @@ } } +extern OCTAVE_API Complex +octave_str2double (const std::string& str_arg); + #endif