HEX String to byte array

wrun
Offline
Зарегистрирован: 03.07.2014

Добрый день!

Никак не могу найти решение:

Есть строка:

String s = "1942E560613C";

Как из нее получить подобное?:

Byte s[6] = { 0x19, 0x42, 0xE5, 0x60, 0x61, 0x3C };

 

axill
Offline
Зарегистрирован: 05.09.2011

посмотрите функцию sscanf, она сканирует из строки, но формат такой же как у потоковой scanf http://avr-libc.narod.ru/glibc-7.html#ss7.11

давно не применял, будет что-то типа такого, за корректность не ручаюсь, прочтете описание - сами подправите:

int s01, s23, s45;

sscanf(s, "%x%x%x", &s01, &s23, &s45);
s[0] = s01 >> 8;
s[1] = s01 & 0xff;
s[2] = s23 >> 8;
s[3] = s23 & 0xff;
s[4] = s45 >> 8;
s[5] = s45 & 0xff;

или так

int s0, s1, s2, s3, s4, s5;

sscanf(s, "%2x%2x%2x%2x%2x%2x", &s0, &s1, &s2, &s3, &s4, &s5);

 

wrun
Offline
Зарегистрирован: 03.07.2014

Благодарю!

Сделал вот так:

    String trs = "ABCDEF89";
    byte dd[4];
    char tr[8];
    trs.toCharArray(tr, 8);
    for (int i = 0; i < 4; i++)
      sscanf(&tr[i * 2], "%2x", &dd[i]);
    dump_byte_array(dd, 4);
    Serial.println();

В результате чуть чуть НЕ верно:

AB CD EF 08

Какое то смещение в конце !?
 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Возможно так (не пробовал):

char tr[9];
...
trs.toCharArray(tr, 9);

Плюс что там в dump_byte_array неизвестно.

 

axill
Offline
Зарегистрирован: 05.09.2011

вы неправильно используете sscanf ибо она работает со строкой заканчивающейся нулем. А ваш массив tr нулем не заканчивается, такой код в принципе может привести к сбою программы. 

Для корректности сделайте массив из 9 символов и в последний элемент запишите ноль

tr[8] = 0;

 

 

Datak
Offline
Зарегистрирован: 09.10.2014

kisoft пишет:
Возможно так (не пробовал)

Да, по-моему так, но тоже не пробовал. :)

toCharArray копирует в буфер строку вместе с завершающим нулём, значит для нуля нужно тоже выделить место.

А вообще, в большинстве случаев нет смысла тратить драгоценную ардуиновскую память, и копировать строку в отдельный буфер. В объекте String точно такой же буфер уже есть, и есть метод для получения указателя на него:





















const char* c_str( ) const; 

Как-то так должно получиться:

















String trs = "ABCDEF89";

byte dd[4];

const char* tr = trs.c_str( );

for (int i = 0; i < 4; i++)
  sscanf(&tr[i * 2], "%2x", &dd[i]);

dump_byte_array(dd, 4);

Serial.println();

Если sscanf будет ругаться, то так: (проверить сейчас не могу)







char* tr = ( char* )( trs.c_str( ) );

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

UPD: А для лентяев исходник:

void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const
{
	if (!bufsize || !buf) return;
	if (index >= len) {
		buf[0] = 0;
		return;
	}
	unsigned int n = bufsize - 1;
	if (n > len - index) n = len - index;
	strncpy((char *)buf, buffer + index, n);
	buf[n] = 0;
}

А чтобы не говорили, что это другой метод, выдержка из WString.h:

	void toCharArray(char *buf, unsigned int bufsize, unsigned int index=0) const
		{getBytes((unsigned char *)buf, bufsize, index);}

Надеюсь теперь понятно, что если указать 8, то в последний байт запишется '\0', а если сделать как я написал, то '\0' запишется в tr[9]; т.е. остальные 8 байт будут содержать нужные данные.

 

Datak
Offline
Зарегистрирован: 09.10.2014

kisoft пишет:
если указать 8, то в последний байт запишется '\0', а если сделать как я написал, то '\0' запишется в tr[9]; т.е. остальные 8 байт будут содержать нужные данные.

Точно, так оно и будет. :)

Но чтобы уж совсем точно - '\0' запишется всё-таки в  tr[8], а остальные 8 байт - это байты с  tr[0]  по  tr[7].

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Datak пишет:

kisoft пишет:
если указать 8, то в последний байт запишется '\0', а если сделать как я написал, то '\0' запишется в tr[9]; т.е. остальные 8 байт будут содержать нужные данные.

Точно, так оно и будет. :)

Но чтобы уж совсем точно - '\0' запишется всё-таки в  tr[8], а остальные 8 байт - это байты с  tr[0]  по  tr[7].

Это да, но кто хотел, тот понял правильно :)

sva1509
Offline
Зарегистрирован: 07.12.2012

Доброго времени суток !

scanf, умножение, Вам программы не под контроллеры 8-ми битные писать, а под win !

typedef struct {
      char a;
      char b;
} Btype;

char *str1={"12345678ABCDEF"}; // строка тестовая

int convert(char *str,unsigned char *byte) // первый параметр указывает на массив символов, второй байт, возвращает колл-во байт
{
        Btype *chp;
        chp = (Btype*)str;
        int i = 0;
        unsigned char c1,b1;
        while(chp[i].a) {
                c1=(chp[i].a >= 65)?55:48;
                b1 = (chp[i].a - c1) << 4;
                c1=(chp[i].b >= 65)?55:48;
                byte[i] = b1 | (chp[i].b - c1);i++;
        }
        return i;
}

в вашем случае вызывать 

i1=convert(s.c_str(),bytes);

 

Datak
Offline
Зарегистрирован: 09.10.2014

sva1509 пишет:
scanf, умножение, Вам программы не под контроллеры 8-ми битные писать, а под win !

Да ладно, стыдно нам... :(

А давайте. Давайте мериться, этими самыми... умением двигать биты, да. :)































inline unsigned char toHex( char ch )
{
   return ( ( ch >= 'A' ) ? ( ch - 'A' + 0xA ) : ( ch - '0' ) ) & 0x0F;
}

// str   - указатель на массив символов
// bytes - выходной буфер
// функция возвращает колл-во байт
//
int convert( char* str, unsigned char* bytes ) 
{
   unsigned char Hi, Lo;

   int i = 0;
   while( ( Hi = *str++ ) && ( Lo = *str++ ) )
   {
      bytes[i++] = ( toHex( Hi ) << 4 ) | toHex( Lo ); 
   }

   return i;
}

 

sva1509
Offline
Зарегистрирован: 07.12.2012

То же весьма красивое решение ! :)

А главное без вызова не нужных функций и долгих операторов. Дело не в мерятся, просто эти посты читают начинающие в основном программисты. По этому подсказывать лучше "правильные" решения. Они на начальном этапе должны учится экономить ресурс. 

axill
Offline
Зарегистрирован: 05.09.2011

sva1509 пишет:

 этапе должны учится экономить ресурс. 

вы куда больше сэкономите если загоните строки в PROGMEM и откажетесь от использования форматирования в Serial

ну а все остальное вопрос компромиса между простотой и эффективностью. При применение sscanf просто и прозрачно и более универсально (можно делать разбор куда более сложных строк). Пример последний с самостоятельной конвертацией более эффективен, менее прозрачен, не универсален.

Даже С++ который использует ардуина это уже значительное перерасходование ресурсов, но зато удобно и упрощает многое и прощает многое новичкам. Впрочем и Си тоже чуть меньше жрет, но куда больше чем ассемблер. Общее правило - чем проще програмисту, тем сложнее железке и наоборот

sva1509
Offline
Зарегистрирован: 07.12.2012

И мой пример и Datak не припядствует использованию PROGMEM для хранения строк. sscanf использует строковый анализатор и еще с десяток функций которые для решения вопроса топикстартера не нужны (по этому функция сильно жирная и по размеру и по быстродействию). Приведенный алгоритм мне кажется достаточно прозрачным, хотя и не имеет защиты от дурака (выдаст не правильные данные при произвольном наборе символов), но мне кажется что для контроллера это нормально. И по моему скромному мнению - универсальность в программировании контроллеров это роскош. То есть, если очень надо - то можно, но лучше не частить.

axill
Offline
Зарегистрирован: 05.09.2011

Если универсальность это роскошь уходите с ардуино. Остальное все компромисс между решаемой задачей, имеющимися ресурсами и сложностью реализации