Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using stringstream instead of `sscanf` to parse a fixed-format string

Tags:

c++

I would like to use the facilities provided by stringstream to extract values from a fixed-format string as a type-safe alternative to sscanf. How can I do this?

Consider the following specific use case. I have a std::string in the following fixed format:

YYYYMMDDHHMMSSmmm

Where:

YYYY = 4 digits representing the year
MM = 2 digits representing the month ('0' padded to 2 characters)
DD = 2 digits representing the day ('0' padded to 2 characters)
HH = 2 digits representing the hour ('0' padded to 2 characters)
MM = 2 digits representing the minute ('0' padded to 2 characters)
SS = 2 digits representing the second ('0' padded to 2 characters)
mmm = 3 digits representing the milliseconds ('0' padded to 3 characters)

Previously I was doing something along these lines:

string s = "20101220110651184";
unsigned year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, milli = 0;    
sscanf(s.c_str(), "%4u%2u%2u%2u%2u%2u%3u", &year, &month, &day, &hour, &minute, &second, &milli );

The width values are magic numbers, and that's ok. I'd like to use streams to extract these values and convert them to unsigneds in the interest of type safety. But when I try this:

stringstream ss;
ss << "20101220110651184";
ss >> setw(4) >> year;

year retains the value 0. It should be 2010.

How do I do what I'm trying to do? I can't use Boost or any other 3rd party library, nor can I use C++0x.

like image 449
John Dibling Avatar asked Dec 28 '10 19:12

John Dibling


2 Answers

Erm, if it's fixed format, why don't you do this?

  std::string sd("20101220110651184");
  // insert spaces from the back
  sd.insert(14, 1, ' ');
  sd.insert(12, 1, ' ');
  sd.insert(10, 1, ' ');
  sd.insert(8, 1, ' ');
  sd.insert(6, 1, ' ');
  sd.insert(4, 1, ' ');
  int year, month, day, hour, min, sec, ms;
  std::istringstream str(sd);
  str >> year >> month >> day >> hour >> min >> sec >> ms;
like image 152
Nim Avatar answered Sep 29 '22 04:09

Nim


One not particularly efficient option would be to construct some temporary strings and use a lexical cast:

std::string s("20101220110651184");
int year = lexical_cast<int>(s.substr(0, 4));
// etc.

lexical_cast can be implemented in just a few lines of code; Herb Sutter presented the bare minimum in his article, "The String Formatters of Manor Farm."

It's not exactly what you're looking for, but it's a type-safe way to extract fixed-width fields from a string.

like image 44
James McNellis Avatar answered Sep 29 '22 02:09

James McNellis