-->

istringstream not honoring base?

2019-08-10 19:58发布

问题:

I'm trying to remediate some Coverity findings on tainted values due to the use of atoi and atof. I switched to an istringstream, but its not producing expected results for bases other than 10.

If I switch to base 16, enter 0xa and avoid the iss.ignore(2);, then the result is 0:

$ ./tt.exe 0xa
X: 0

If I switch to base 16, enter 0xa and utilize the iss.ignore(2);, then the result is an exception:

$ ./tt.exe 0xa
'0xa' is not a value

I visited CPP Reference on istringstream as recommended by @πάντα, but it does not discuss a limitation in this context.

Any ideas what I am doing wrong? Or, how can I get this to work as expected?


$ cat tt.cxx
#include <iostream>
#include <sstream>
#include <iomanip>
#include <stdexcept>
using namespace std;

template <class T>
T StringToValue(const std::string& str) {
  std::istringstream iss(str);
  T value;

  if (str.length() >= 2) {
    if (str[0] == '0' && (str[1] =='X' || str[1] =='x'))
      {
        iss.setf(std::ios_base::hex);
        iss.ignore(2);
      }
  }
  iss >> value;

  if (iss.fail())
    throw runtime_error("'" + str +"' is not a value");

  return value;
}

int main(int argc, char* argv[])
{
  try
    {
      int x = StringToValue<int>(argc >= 2 ? argv[1] : "ZZZ...");
      cout << "X: " << x << endl;
    }
  catch(const runtime_error& ex)
    {
      cerr << ex.what() << endl;
      return 1;
    }

  return 0;
}

回答1:

You're completely overthinking this. Reading a value in hexadecimal notation is easy.

#include <sstream>
#include <iomanip>
#include <cassert>

int main()
{
   {
      int x = 0;
      std::istringstream is("0xa");
      is >> std::hex >> x;
      assert(x == 10);
   }
   {
      int x = 0;
      std::istringstream is("a");
      is >> std::hex >> x;
      assert(x == 10);
   }
}

(live demo)


Regardless, I can identify two problems with your code.

1. Incorrect use of std::ios_base::setf

You're replacing the stream's entire flagset with only std::ios_base::hex. The flags are not just for numeric bases. To do that, you need to mask out everything else to prevent unsetting other, unrelated, but necessary flags (I don't know what they are):

iss.setf(std::ios_base::hex, std::ios::basefield);

This is why iss >> std::hex is so much easier.

It's also why you should have constructed a minimal testcase consisting of nothing but a test of iss.setf before posting!

2. Broken logic

The case where input is just "a" skips that statement entirely, as it is conditional on the first two characters being "0x".

Move it to just before the iss >> value, I would.


You can see your fixed code here but, as explored at the start of my answer, the whole switching-on-leading-"0x" thing is unnecessary, so most of your code can just be removed.



标签: c++ base istream