Кодирование, декодирование целого числа в массив символов

Обратите внимание, что это не домашнее задание, и я искал перед тем, как начать эту новую ветку. Я получил Сохранить int в массиве символов?

Я искал ответ, но не получил удовлетворительного ответа в приведенной выше теме.

Вот мое требование: я хочу закодировать свои данные (скажем, целое число) в массиве байтов, а затем передать их по сети, а затем декодировать на другом конце и обработать.

Вот часть кодирования:

const int MAX=5;
uint32_t a = 0xff00ffaa;
char byte_array[1024]; // this is the array to be transferred over the network
char buff[MAX]="";
sprintf(buff,"%4d",a);
memcpy(byte_array,buff,4);
// fill remaining stuff in the byte array and send it over the network

Вот часть декодирования:

const int MAX=5;
char buff[MAX]="";
strncat(buff,byte_array,4)

int i=atoi(buff);
// Work with i

Вот мои вопросы:

1) Переносим ли приведенный выше код? Я думаю, это так (поправьте меня, пожалуйста)

2) Теперь я хочу закодировать массив байтов с 3 байтами (но целочисленный размер равен 4), то есть сказать, что целое число хранит 0x00ffaabb, и я просто хочу, чтобы массив байтов имел индекс ff int 0th aa в 1-м индексе и bb в 2-й индекс. Как это сделать?

snprinf, похоже, не работает или я что-то упускаю.

Мне легко может помочь человек, который реализовал какой-либо сетевой протокол. Думаю, логика декодирования все равно будет работать. (strncat(buff,byte_array,3), за которым следует atoi вызов функции).

Вот что говорится в протоколе:

    --------+--------+--------+--------+------------------------------
    |Version|   3       byte    length |  Remaining stuff
    --------+--------+--------+--------+------------------------------

Версия - это 1 байт, за которым следуют 3 байта сообщения.

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


person ghayalcoder    schedule 09.11.2009    source источник
comment
Спасибо всем за оперативную помощь. Я понял. Я как бы реализую протокол из RFC, и мне нужна была вся эта информация. Большое спасибо :)   -  person ghayalcoder    schedule 10.11.2009


Ответы (10)


Вы сохраняете как ASCII, где вы должны хранить сами байты.

Кодировка должна быть примерно такой:

uint32_t a = 0xff00ffaa;
unsigned char byte_array[1024];

Обратите внимание, как я сделал ваш целевой массив беззнаковым, чтобы указать, что это «необработанные байты», а не символы.

byte_array[0] = a >> 24;
byte_array[1] = a >> 16;
byte_array[2] = a >> 8;
byte_array[3] = a >> 0;

Это сериализует переменную a в четыре первых байта byte_array, используя порядок байтов с прямым порядком байтов, который является своего рода значением по умолчанию для многих сетевых протоколов.

Вы также можете увидеть мой ответ здесь: вопрос 1577161 .

person unwind    schedule 09.11.2009

1) это своего рода работа, поскольку вы используете массив символов для транспортировки, я бы лично использовал двоичный протокол. Если вы можете использовать 4 байта своей переменной, я бы посмотрел на функции htonl / ntohl (они есть практически в каждом unix и в Windows, начиная с w2k), иначе см. Ниже

2) с двоичным протоколом кодирование будет

uint32_t a = 0xff00ffaa;
char byte_array[1024]; // this is the array to be transferred over the network

// leave byte_array[0] for version byte
// leave the high order byte in a since you want only the 3 lowest
byte_array[1] = (char)((a & 0x00FF0000)>>16);
byte_array[2] = (char)((a & 0x0000FF00)>>8);
byte_array[3] = (char)(a & 0x000000FF);

и декодирование будет

uint32_t a = 0;
a |= byte_array[1]<<16;
a |= byte_array[2]<<8;
a |= byte_array[3];
person Aszarsha    schedule 09.11.2009
comment
Ух - так много неправильного в этом примере. 1. Если вы собираетесь использовать процедуры трансляции сети (htonl () и другие), вы ДОЛЖНЫ передать всю переменную. На машине с прямым порядком байтов в этом примере будет отброшен младший байт (в данном случае 0xaa). 2. Если вы собираетесь передавать, удаляя байты самостоятельно, тогда нет необходимости использовать htonl () - эта функция гарантирует, что двоичный формат с порядком байтов соответствует сетевому формату. При удалении байтов вы ОПРЕДЕЛЯЕТЕ формат байтов. - person Aaron; 09.11.2009
comment
Вы правы насчет измельчения, я объединяю первый пример с htonl, но беру старший байт, а другой без htonl, но без старшего байта, как запрошено в вопросе. Я отредактирую свой ответ, чтобы убрать звонки на htonl. - person Aszarsha; 09.11.2009

То, что вы делаете, будет своего рода работой. Вы не передаете байты данных - вы передаете числовое значение данных. В результате буфер размером 5 слишком мал для данных, которые вы отправляете (0xFF00FFAA имеет числовое значение 4278255530 - 10 байт).

Чтобы передать байты, вам нужно сделать что-то вроде следующего (предполагается, что прямой порядок байтов):

Кодировать:

char array[1024]; // outgoing network data
int next = 0;

array[next++] = value & 0xFF;
array[next++] = (value >> 8) & 0xFF;
array[next++] = (value >> 16) & 0xFF;
array[next++] = (value >> 24) & 0xFF;

Эти операторы удаляют байты значения и присваивают их последовательным значениям в вашем массиве.

Расшифровать:

char array[1024]; // incoming network data
int next = 0;

value = 0;
value |= (int)*((unsigned char*)array)[next++];
value |= (int)*((unsigned char*)array)[next++] << 8;
value |= (int)*((unsigned char*)array)[next++] << 16;
value |= (int)*((unsigned char*)array)[next++] << 24;

Эти операторы извлекают байты из массива и возвращают их в значение.

Если вы хотите попытаться оптимизировать сетевой формат и не передавать байты, вы можете удалить часть данных. Но помните, что ваш отправитель и получатель должны знать друг от друга, чего ожидать, поэтому должна быть некоторая информация о типе или длине передаваемых элементов данных.

person Aaron    schedule 09.11.2009

По крайней мере, чтобы быть переносимым, вы должны подумать о возможном другом порядке байтов при кодировании.

Вам действительно нужно внедрить новый сетевой протокол обмена сообщениями? Вам не подходят NASA IPC или Sun RPC? Оба они достаточно стабильны, NASA проще запустить, RPC кажется более доступным (да, он готов к использованию, и для большинства популярных систем доступна библиотека).

  • Для RPC попробуйте man rpc
  • Информацию о IPC НАСА см. здесь
person Roman Nikitchenko    schedule 09.11.2009

Возможно, вам нужно заставить это работать с существующим протоколом, и в этом случае проигнорируйте мой ответ.

Вместо того, чтобы изобретать велосипед здесь, почему бы вам не использовать библиотеку Google Protocol Buffers для выполнения эта работа? Проще, гибче и очень эффективно.

person Michael Dillon    schedule 09.11.2009

Используйте XDR (RFC 4506).

person Sinan Ünür    schedule 09.11.2009

То, что у вас есть, не будет работать так, как это есть у вас. Например, a является 32-битным, и в вашем примере вы устанавливаете старшие биты, что означает, что он не может поместиться в 4-значное число с вашим оператором printf. (0xff00ffaa = 4278255530, что превышает 4 цифры) Я считаю, что это приведет к переполнению буфера. Я считаю, что printf преобразует его и переполнит поле, но это зависит от того, как ваш компилятор / C реализует функцию printf, когда не хватает места в буфере.

Для имеющегося у вас оператора printf максимальное значение, которое вы можете передать, будет 9999 для 4 символов. Аналогичным образом, в вашем примере передачи данных с полем длины 3 байта максимальная длина будет 999. Теоретически ваша длина может быть 1000, если вы добавите 1 к длине, но объявленный вами буфер равен 1024. где максимальная длина буфера, которая вам потребуется, будет составлять 1004 байта.

Использование символов ASCII действительно делает сообщения / данные переносимыми в системе, но это происходит за счет использования большей полосы пропускания / пространства, а также времени и усилий на программирование для преобразования туда и обратно из ASCII для передачи данных.

Похоже, у вас есть хорошая идея, но над ней еще нужно немного поработать.

person Glenn    schedule 09.11.2009

Вероятно, лучше всего использовать какой-нибудь существующий инструмент. Если не можете - заботитесь ли вы о порядке байтов (т.е. это кроссплатформенный протокол?)

В противном случае вы можете просто сделать что-то вроде ...

unsigned char msg[1024];
int writeIndex = 0;
[...]
int mynum  = 12345;
memcpy(msg + writeIndex , &mynum, sizeof mynum);
writeIndex += sizeof mynum;

и расшифровать

//[...] also declare readIndex;
memcopy(&mynum, msg + readIndex, sizeof mynum);
readIndex += sizeof mynum;

(вы можете заменить понятие msg + index указателем на беззнаковый символ, хотя это вряд ли имеет значение).

Подобное использование memcpy может быть медленнее, но также более читаемым. При необходимости вы можете реализовать клон memcopy в #define или встроенной функции - в конце концов, это всего лишь короткий цикл присваиваний.

person Eamon Nerbonne    schedule 09.11.2009

Использование функции atoi оправдано только в том случае, если строка, которую вы ожидаете декодировать, была построена вашим собственным кодом и не более чем на пару строк выше. То есть его можно использовать только в скетч-коде.

В противном случае, особенно в вашем случае, когда данные поступают из сети, функция atoi не может быть осмысленно использована для выполнения декодирования, поскольку она не обеспечивает полезного механизма обработки ошибок и абсолютно никакой защиты от переполнения (неопределенное поведение при переполнении). Единственная функция, которую можно эффективно использовать для преобразования строки в целое число, - это функция из группы strto..., в вашем случае strtol.

person AnT    schedule 09.11.2009

Я просматривал эту страницу миллион раз и очень благодарен всем остальным ответам за то, что они мне помогли. Вот заглушка, которую я использую, которая отличается от других ответов, потому что ее можно использовать в цикле for:

void encode_int_as_char(int num, char *buf, int length){
    int i;
    for (i = 0; i < length; i++){
        buf[i] = (char)(num >> ((8 * (length - i - 1)) & 0xFF));
    }
}

int decode_int_from_char(char *enc, int length){
    int i, num, cur;

    num = 0;
    for (i = 0; i < length; i++){
        cur = (unsigned char) enc[i] << (8 * (length - i - 1));
        num += (int) cur;
    }

    return num;
}
person Goodword    schedule 03.02.2019