c++widthfieldistringstreamsetw

Reading int with specific field width from stringstream


I'm trying to read bytes in hex notation from a string. The bytes may or may unfortunately not be separated by whitespace, e.g. " 00 ffab c " is a valid example and should result in 4 bytes read, 0x00, 0xff, 0xab and 0x0c. The problem is to skip whitespace but read only two adjacent digits, when present.

If the input were from a file, the task would be as easy as while(fscanf(f, "%2d", &i) == 1) ... because sscanf skips whitespace, the read position is tracked by the underlying FILE, and the maximum field width is only applied to the item read, not the raw input characters containing the whitespace. But the position tracking is not possible when reading from a string; I need to use the %n format conversion specifier which stores the number of characters read so far by this invocation into the associated variable, e.g. scanf(f, "%2d%n", &i, &readIncr), and manually maintain a read position by adding the respective increments.

This is somewhat cumbersome, hence I wanted to use std::istringstream which does keep track of a position in the underlying string.

But setting a width on the input stream does not have the desired (and expected) effect; below is a minimal demonstration; for simplicity I'm using decimal integers. Documentation and examples for an input field width are scarce.

Am I doing something wrong? I this use case simply not intended?

#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdio>

using namespace std;

int main()
{
  const char *s = "   1234";
  int i;
  istringstream is(s);

  if (is >> setw(2) >> i)
  {
    cout << "stringstream i: " << i << '\n';
  }

  if (sscanf(s, "%2d", &i) == 1)
  {
    cout << "scanf i: " << i << '\n';
  }
}

The output is (with g++ and MSVC)

$ g++ -Wall -o fieldwidth fieldwidth.cpp && ./fieldwidth
stringstream i: 1234
scanf i: 12

Solution

  • Sadly cpp streams are far from perfect. AFAIK std::setw works only for reading strings. What you can do is:

    #include <iostream>
    #include <sstream>
    #include <iomanip>
    #include <cstdio>
    
    using namespace std;
    
    int main()
    {
      const char *s = "   1234";
      std::string i;
      istringstream is(s);
    
      if (is >> setw(2) >> i)
      {
        cout << "stringstream i: " << std::stoi(i) << '\n';
      }
    
      int j;
      if (sscanf(s, "%2d", &j) == 1)
      {
        cout << "scanf i: " << j << '\n';
      }
    }
    

    And you get expected output:

    stringstream i: 12
    scanf i: 12