Suggestion about std::string conversions

B

Barzo

Hi,

I'm a quite newbie so... :)

I have a char* buffer = [1F][4D][BC][77][80] that represent a
timestamp with this convention:

// 00011111 01001101 10111100 01110111 10000000 -> 1F 4D BC 77
80
// | 2003 | 06 | 30 | 07 | 30 | 00 |

and I have to build a function that returns an std::string with value
"20030630073000" (YYYYMMDDhhmmss).

I have tought to do something like this:

std::string decode_time(const std::string& buffer)
{
double time;
std::stringstream ret_val;

time = (buffer[0] * 0x100000000) +
(buffer[1] * 0x1000000) +
(buffer[2] * 0x10000) +
(buffer[3] * 0x100) +
buffer[4];

unsigned short year = (((buffer[0] * 0x100) + buffer[1]) >> 2);
unsigned short month = ((((buffer[1] & 0x03) * 0x100) + (buffer[2]
& 0xC0) ) >> 6);
unsigned short day = (buffer[2] & 0x3E) >> 1;
unsigned short hour = ((((buffer[2] & 0x01) * 0x100) + (buffer[3]
& 0xF0) ) >> 4);
unsigned short minute = ((((buffer[3] & 0x0F) * 0x100) + (buffer[4]
& 0xC0) ) >> 6);
unsigned short second = (buffer[4] & 0x3F);

ret_val << year << month << day << hour << minute << second ;

return ret_val.str();
}

but I think there are more efficient ways..
Also..
- Could I substitute the time = (...) instruction with memcpy(&time,
buffer, 5) ?
- With this method I can't have months, days, hous always with 2
digits:
if I have month = 6 I have to encode it how "06"...

Tnx,
Daniele.
 
K

Kai-Uwe Bux

Barzo said:
Hi,

I'm a quite newbie so... :)

I have a char* buffer = [1F][4D][BC][77][80] that represent a
timestamp with this convention:

// 00011111 01001101 10111100 01110111 10000000 -> 1F 4D BC 77
80
// | 2003 | 06 | 30 | 07 | 30 | 00 |

and I have to build a function that returns an std::string with value
"20030630073000" (YYYYMMDDhhmmss).

I have tought to do something like this:

std::string decode_time(const std::string& buffer)
{
double time;
std::stringstream ret_val;

time = (buffer[0] * 0x100000000) +
(buffer[1] * 0x1000000) +
(buffer[2] * 0x10000) +
(buffer[3] * 0x100) +
buffer[4];

a) A double is not guaranteed to have the required precision.
b) You don't use the variable double anyway.
unsigned short year = (((buffer[0] * 0x100) + buffer[1]) >> 2);
unsigned short month = ((((buffer[1] & 0x03) * 0x100) + (buffer[2]
& 0xC0) ) >> 6);
unsigned short day = (buffer[2] & 0x3E) >> 1;
unsigned short hour = ((((buffer[2] & 0x01) * 0x100) + (buffer[3]
& 0xF0) ) >> 4);
unsigned short minute = ((((buffer[3] & 0x0F) * 0x100) + (buffer[4]
& 0xC0) ) >> 6);
unsigned short second = (buffer[4] & 0x3F);

ret_val << year << month << day << hour << minute << second ;

That seems false: what happens if month=1, day=21, and hour=1? I do not see
why leading 0s would magically appear, so the return value would be

...1212...

and nobody could tell whether it is 12,1,2; or 1,21,2; or 1,2,12.

return ret_val.str();
}

but I think there are more efficient ways..

Don't worry about efficiency before the code is correct.
Also..
- Could I substitute the time = (...) instruction with memcpy(&time,
buffer, 5) ?

No. But you can just get rid of that variable altogether.
- With this method I can't have months, days, hous always with 2
digits:
if I have month = 6 I have to encode it how "06"...

Ah, you noticed too. Well, you can. Just read up on manipulators. Something
like std::setw(2) should do the trick.


Best

Kai-Uwe Bux
 
B

Barzo

  time = (buffer[0] * 0x100000000) +
         (buffer[1] * 0x1000000) +
         (buffer[2] * 0x10000) +
         (buffer[3] * 0x100) +
          buffer[4];

a) A double is not guaranteed to have the required precision.
b) You don't use the variable double anyway.
Also..
- Could I substitute the time = (...) instruction with memcpy(&time,
buffer, 5) ?

No. But you can just get rid of that variable altogether.

Ops...I'm sorry!!
This is a cut&paste mistake!
This instructions, of course, does not exists!

Ah, you noticed too. Well, you can. Just read up on manipulators. Something
like std::setw(2) should do the trick.

Oh! I will look it!

Thanks a lot!
Daniele.
 
K

Kai-Uwe Bux

Barzo wrote:
[snip]
Oh! I will look it!

Now that I think about it, maybe you need a setfill('0'), too, in order to
specify that the field should be filled with 0s. Maybe there is yet a third
manipulator involved to say whether the contents goes to the left or right.


Best

Kai-Uwe Bux
 
B

Barzo

Now that I think about it, maybe you need a setfill('0'), too, in order to
specify that the field should be filled with 0s. Maybe there is yet a third
manipulator involved to say whether the contents goes to the left or right.

Yes!
With the follow instruction the output is correct:

ret_val << year << std::setw(2) << std::setfill('0')
<< month << std::setw(2) << std::setfill('0')
<< day << std::setw(2) << std::setfill('0')
<< hour << std::setw(2) << std::setfill('0')
<< minute << std::setw(2) << std::setfill('0')
<< second;

But this code is efficiency?

Tnx!
Daniele.
 
K

Kai-Uwe Bux

Barzo said:
Yes!
With the follow instruction the output is correct:

ret_val << year << std::setw(2) << std::setfill('0')
<< month << std::setw(2) << std::setfill('0')
<< day << std::setw(2) << std::setfill('0')
<< hour << std::setw(2) << std::setfill('0')
<< minute << std::setw(2) << std::setfill('0')
<< second;

But this code is efficiency?

Only measurement can tell for sure. Also, alternatives are likely to do the
same kind of conversions and formatting under the hood. So, you probably
will not be able to save much.

One thing, you could add is a call

ret_val.reserve( 4+2+2 + 2+2+2 );

before you do the above. That way, reallocation of the string during the
formatting can be avoided. You are in the lucky position to know the length
of the output in advance.


Best

Kai-Uwe Bux
 
G

Gert-Jan de Vos

I have a char* buffer = [1F][4D][BC][77][80] that represent a
timestamp with this convention:

    // 00011111 01001101 10111100 01110111 10000000 -> 1F 4D BC 77
80
    // |     2003    | 06 | 30 |  07 |  30  |  00 |

and I have to build a function that returns an std::string with value
"20030630073000" (YYYYMMDDhhmmss).

Good old sprintf() can be better readable and very efficient in these
cases. Take care that your function uses unsigned char or your
calculations may produce negative numbers.

std::string decode_time(const unsigned char (&data)[5])
{
int year = (data[0] << 6) | (data[1] >> 2);
int month = ((data[1] & 0x03) << 2) | (data[2] >> 6);
int day = (data[2] & 0x3E) >> 1;
int hour = ((data[2] & 0x01) << 4) | (data[3] >> 4);
int minute = ((data[3] & 0x0F) << 2) | (data[4] >> 6);
int second = data[4] & 0x3F;

char str[4 + 2 + 2 + 2 + 2 + 2 + 1];
sprintf(str, "%04d%02d%02d%02d%02d%02d", year, month, day, hour,
minute, second);
return str;
}
 
B

Barzo

One thing, you could add is a call

  ret_val.reserve( 4+2+2 + 2+2+2 );

before you do the above. That way, reallocation of the string during the
formatting can be avoided. You are in the lucky position to know the length
of the output in advance.

Great! I'll do it!
I have to learn a lot on STL!

Tnx!
Daniele.
 
B

Barzo

Good old sprintf() can be better readable and very efficient in these
cases. Take care that your function uses unsigned char or your
calculations may produce negative numbers.

Yes, this is another good solution.
Usually I try to avoid using old c-like functions like sprintf, but in
this case it may be more efficiency!

Tnx.
Daniele.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,104
Latest member
LesliVqm09
Top