0

I'm currently working with a pre-existing C++ code that parses Fortran-output ASCII of floating-point numbers. The Fortran code that produces the values uses 2-digit exponential output. So for extreme values, it will necessarily omit the e. Changing to 3-digit output in the Fortran is not an option. Thus, I have mixed scientific-formatted values, some with e and some without. This confounds my C++ parsing and leads to the question:

How can I send a stringstream of the mixed 2/3-digit floating-point values to a C++ array of doubles?

My thought is to write an IO manipulator to take the extracted output from the stringstream of values and search for all - or + with a preceding digit and insert (putback?) an e so that when it's extracted to a C++ double, it will be registered correctly. However, everything I've tried has lead to bogus values. As close as I've gotten without getting to that point is below.

#include <iostream>
#include <sstream>

std::istream &scrub(std::istream &is) {
  std::istream::sentry s(is);
  //  ???
  return is;
}

int main() {

  std::istringstream input("+1.01-101 -2.02e+20\n"
                           "+3.03e+30 -4.04+104");
  double vals[4];

  for (int i = 0; i < 4; ++i)
    input >> scrub >> vals[i];
  for (int i = 0; i < 4; ++i)
    std::cout << vals[i] << std::endl;

  return 0;
}

The current output is:

1.01
-101
-2.02e+20
3.03e+30

because of the missing "e" to let the extractor properly parse into the C++ double. The desired output is:

1.01e-101
-2.02e+20
3.03e+30
4.04e+104

Notes:

  1. The input stream may be 100s MBs to GBs, which is why streams were chosen as away to interact with the data rather than large string objects (perhaps with regular expression adjustment).
  2. I cannot move away from the input >> vals[i] approach because doing so will lead to substantial rewriting of the remaining C++ in the actual application.
  3. C++11 capabilities are available to me, but I (unfortunately) cannot go to a newer standard.

Related:

Joel Kulesza
  • 212
  • 3
  • 7

1 Answers1

0

The approach could be to first capture in a string each element, then regex as needed before reading to double.

Something like :

string tmp;
cin >> tmp; // break on space
std::regex re ("(\\d)(\\+|\\-)"); // digit followed by + or -
regex_replace (tmp, re, "\\1e\\2"); // add e between 1 and 2
stringstream ss (tmp);
tmp >> values[i];
Yann TM
  • 1,942
  • 13
  • 22
  • I’m streaming in from a file, so to be sure I understand, you recommend stream -> string -> regex -> stream? Because of the simplicity of the regex operation I’d hope to do it as part of then streaming, that is, insert an “e” if a “-“ or “+” is encountered with a digit preceding. – Joel Kulesza Sep 13 '20 at 13:33
  • well, you need to manipulate the string a bit, so yes we need a tmp string in memory. For the last parse stringstream is more homogeneous to your code, but yes http://www.cplusplus.com/reference/cstdlib/strtod/ `strtod` might be easier. – Yann TM Sep 15 '20 at 07:54
  • so stream->string->regex->parse is more accurate – Yann TM Sep 15 '20 at 07:59