tags:

views:

4003

answers:

5

Hi all,

I am interfacing with an external device which is sending data in hex format. It is of form

> %abcdefg,+xxx.x,T,+yy.yy,T,+zz.zz,T,A*hhCRLF
  • CR LF is carriage return line feed
  • hh->checksum
  • %abcdefg -> header

Each character in above packet is sent as a hex representation (the xx,yy,abcd etc are replaced with actual numbers). The problem is at my end I store it in a const char* and during the implicit conversion the checksum say 0x05 is converted to \0x05. Here \0 being null character terminates my string. This is perceived as incorrect frames when it is not. Though I can change the implementation to processing raw bytes (in hex form) but I was just wondering whether there is another way out, because it greatly simplifies processing of bytes. And this is what programmers are meant to do.

Also in cutecom (on LINUX RHEL 4) I checked the data on serial port and there also we noticed \0x05 instead of 5 for checksum. Note that for storing incoming data I am using

//store data from serial here
unsigned char Buffer[SIZE];  

//convert to a QString, here is where problem arises 
QString str((const char*)Buffer); of \0

QString is "string" clone of Qt. Library is not an issue here I could use STL also, but C++ string library is also doing the same thing. Has somebody tried this type of experiment before? Do share your views.

EDIT

This is the sample code you can check for yourself also:

#include <iostream>
#include <string>
#include <QString>
#include <QApplication>
#include <QByteArray>

using std::cout;
using std::string;
using std::endl;

int main(int argc,char* argv[])
{
    QApplication app(argc,argv);
    int x = 0x05;
    const char mydata[] = {
         0x00, 0x00, 0x03, 0x84, 0x78, 0x9c, 0x3b, 0x76,
            0xec, 0x18, 0xc3, 0x31, 0x0a, 0xf1, 0xcc, 0x99};
    QByteArray data = QByteArray::fromRawData(mydata, sizeof(mydata));
    printf("Hello %s\n",data.data());
    string str("Hello ");
    unsigned char ch[]={22,5,6,7,4};
    QString s((const char*)ch);
    qDebug("Hello %s",qPrintable(s));
    cout << str << x ;
    cout << "\nHello I am \0x05";
    cout << "\nHello I am " << "0x05";
    return app.exec();
}
A: 

I think c++ string classes are usually designed to handle zero-terminated char sequences. If your data is of known length (as it appears to be) then you could use a std::vector. This will provide some of the functionality of a string class, whilst ignoring nulls within data.

jon hanson
`std::string` can deal with char sequences containing `'\0'` very well, actually.
sbi
Define "Very well". Create a 9 char string with a null in the middle - "abcd\0efgh". Every operation on the string will only consider the first 4 chars - length will return 4, etc.
jon hanson
That's not the case - std::string is indeed quite capable of holding binary data. E.g., str.size() will return 9 in your example above.
igor
+3  A: 
QByteArray text = QByteArray::fromHex("517420697320677265617421");
text.data();            // returns "Qt is great!"
ufukgun
I checked and text.data() as suggested by you give same issues because it returns a char* or const char*:
rocknroll
A: 

As I see you want to eliminate control ASCII symbols. You could do it in the following way:

#include <iostream>
#include <string>
#include <QtCore/QString>
#include <QtCore/QByteArray>

using namespace std;

// test data from your comment
char data[] = { 0x49, 0x46, 0x50, 0x4a, 0x4b, 0x51, 0x52, 0x43, 0x2c, 0x31,
                0x32, 0x33, 0x2e, 0x34, 0x2c, 0x54, 0x2c, 0x41, 0x2c, 0x2b,
                0x33, 0x30, 0x2e, 0x30, 0x30, 0x2c, 0x41, 0x2c, 0x2d, 0x33,
                0x30, 0x2e, 0x30, 0x30, 0x2c, 0x41, 0x2a, 0x05, 0x0d, 0x0a };

// functor to remove control characters
struct toAscii
{
  // values >127 will be eliminated too
  char operator ()( char value ) const { if ( value < 32 && value != 0x0d && value != 0x0a ) return '.'; else return value; }
};

int main(int argc,char* argv[])
{
  string s;
  transform( &data[0], &data[sizeof(data)], back_inserter(s), toAscii() );

  cout << s; // STL string

  // convert to QString ( if necessary )
  QString str = QString::fromStdString( s );
  QByteArray d = str.toAscii();

  cout << d.data(); // QString

  return 0;
}

The code above prints the following in console:

IFPJKQRC,123.4,T,A,+30.00,A,-30.00,A*.

If you have continuous stream of data you'll get something like:

IFPJKQRC,123.4,T,A,+30.00,A,-30.00,A*.
IFPJKQRC,123.4,T,A,+30.00,A,-30.00,A*.
IFPJKQRC,123.4,T,A,+30.00,A,-30.00,A*.
IFPJKQRC,123.4,T,A,+30.00,A,-30.00,A*.
IFPJKQRC,123.4,T,A,+30.00,A,-30.00,A*.
Kirill V. Lyadvinsky
there in lies the problem jlaep. I wanted 5 after A* and it got printed ".". Do you understand the issue now. 5 is the checksum which is given as 0x05 but isn't printed
rocknroll
If checksum will be 0x49 for instance, then what you expecting to be printed? 'I' or `49`?
Kirill V. Lyadvinsky
I expect it to print character 1
rocknroll
The problem not with std::string or QString. It is with output functions. You should use functions that able to represent characters with codes less then 32(0x20 in hex). It is possible to print such characters using ncurses library in console. You'll get character representation according to ASCII table. For 0x05 you'll get `♣` (hope SO will show this character correctly).
Kirill V. Lyadvinsky
And about 0x49. 0x49 (HEX) is 73 (DEC). It is I. 1 has ASCII code 0x31 (HEX) = 49 (DEC).
Kirill V. Lyadvinsky
A: 

If your 0x05 is converted to the char '\0x05', then you're not having hexadecimal values (that only makes sense if you have numbers as strings anyway), but binary ones. In C and C++, a char is basically just another integer type with very little added magic. So if you have a 5 and assign this to a char, what you get is whatever character your system's encoding defines as the fifth character. (In ASCII, that would be the ENQ char, whatever that means nowadays.)

If what you want instead is the char '5', then you need to convert the binary value into its string representation. In C++, this is usually done using streams:

const char ch = 5; // '\0x5'
std::ostringstream oss;
oss << static_cast<int>(ch);
const std::string& str = oss.str(); // str now contains "5"

Of course, the C std library also provides functions for this conversion. If streaming is too slow for you, you might try those.

sbi
A: 

☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓↓→←∟↔▲▼ "#$%&'()-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѺ¿⌐¬½¼¡

Thats the first 175 for US keyboards :P graphical representation always help

ah ha