обмен байтов и C ++ / C - PullRequest
       5

обмен байтов и C ++ / C

1 голос
/ 30 ноября 2010

Я сделал сообщение на comp.lang.c ++ и получил это

http://groups.google.com/group/comp.lang.c++/browse_thread/thread/afc946826945bdb1/90850f6a0e0edd2d#90850f6a0e0edd2d

но это еще не ответ.

У меня небольшая путаница с бинарной операцией чтения.

Я пытаюсь прочитать двоичный файл с функциями потока. Это файл результатов коммерческой программы (ANSYS), и я знаю структуру файла, по крайней мере, из руководства.

Файл структурирован как записи, а программа написана на фортране. Таким образом, структура похожа на

Длина записи (int) фиктивное целое число данные (может быть int, double) фиктивное целое число

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

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

Я не понял, почему я должен поменять местами байты, потому что этот файл создается на той же машине, и они должны использовать одни и те же системные подпрограммы, так что это не должно быть проблемой, но не похоже, что это дело. Там что-то еще происходит. Я не мог этого понять. Может ли программное обеспечение принудительно поменять байты, что мне было бы трудно понять причину?

Любые комментарии приветствуются.

Вот наивный тестовый пример

int main () {
  ifstream myfile;
  char intBuffer[4];
  myfile.open ("truss.rst", ios::binary);
  myfile.read(intBuffer, sizeof(int));
  //cout << *((int*)intBuffer) << endl;
  // if I do not use this portion-
  // I do not get what I want
  char *cptr, tmp;
  tmp = intBuffer[0];
  intBuffer[0] = intBuffer[3];
  intBuffer[3] = tmp;
  tmp = intBuffer[1];
  intBuffer[1] = intBuffer[2];
  intBuffer[2] = tmp;
  // -----------------------------
  cout << *((int*)intBuffer) << endl;

  myfile.close();
  return 0;
}

Лучший, U.

Ответы [ 8 ]

6 голосов
/ 30 ноября 2010

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

Я бы предложил вам использовать функции ntohl() и ntohs(), которые понятнее, чем ваша обменная процедура.

4 голосов
/ 30 ноября 2010

Независимо от формата, он, очевидно, будет одинаковым на всех компьютерах (было бы забавно, если бы вы не могли открыть файл на другом компьютере).

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

3 голосов
/ 30 ноября 2010

Для программного обеспечения весьма распространено принятие определенного порядка байтов, чтобы сделать двоичные файлы более переносимыми, даже если программное обеспечение еще не поддерживает другие платформы или никогда не сможет. Аналогично, программное обеспечение может использовать библиотеку сериализации, разработанную для переносимости. Такие процедуры, как ntohl(), могут помочь вам восстановить порядок, который вы хотите.

2 голосов
/ 30 ноября 2010

Может быть, программное обеспечение выполняет эту «странную» операцию для поддержки архитектуры с прямым и младшим порядком байтов (порядок байтов различается).

Вывод:

  • На двух разных компьютерах (мало/ big endian) если вы вставите двоичную информацию в ваш файл с одним и тем же вводом, файл может отличаться.
1 голос
/ 30 ноября 2010

Известны случаи, когда вы добровольно устанавливаете один байтный порядок: когда данные предназначены для обмена между машинами, чей порядок байтов неизвестен при запуске, например, через сеть.Вот почему существуют C-примитивы, такие как ntohl и htonl: если сетевая бесконечность такая же, как машинная, они ничего не делают, в противном случае они меняют байты.Здесь может быть что-то похожее, если предполагается, что файлы должны обмениваться между компьютерами.

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

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

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

приведенный ниже код должен работать как шаблон для любого типа, для которого вы хотите изменить порядковый номер, включая double.

#include <stdio.h>
#include <arpa/inet.h>
#include <stdint.h>

template <class builtin>
builtin ntoh(const builtin input) {
    if ((int)ntohs(1) != 1){
        union {
            char buffer[sizeof(builtin)];
            builtin data;
        } in, out;
        in.data = input;
        for (int i = 0 ; i < sizeof(builtin); i++){
            out.buffer[i] = in.buffer[sizeof(builtin) - i - 1];
        }
        return out.data;
    }
    return input;
}

main(){
    printf ("78563412 expected, got: output= %x\n", ntoh<uint32_t>(0x12345678));
}

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

1 голос
/ 30 ноября 2010

Это проблема с порядком байтов . Процессор Intel использует little-endian. «Сетевой порядок байтов» / SPARC / Motorola использует big endian. Многие устаревшие переносимые приложения сохраняют файлы с прямым порядком байтов для обеспечения совместимости.

1 голос
/ 30 ноября 2010

некоторые форматы файлов требуют, чтобы порядок байтов был одинарным, обычно с прямым порядком байтов, так как это сетевой порядок, поэтому на байтах x86 с прямым порядком байтов эти файлы заменяются байтами целых чисел при записи и обратно при чтении

0 голосов
/ 30 ноября 2010

htonl (хост с длинной сетью) и htons (короткая сеть с хостом) будут переходить с любой платформы, на которой вы находитесь, на big-endian. Это было потому, что в те дни большинство сетевых хостов работали в форме UNIX, в которой использовался нативный big-endian.

ntohl и ntohs преобразуют big endian в native независимо от вашей платформы. Если вы находитесь на платформе с прямым порядком байтов, это будет неактивный оператор.

Помимо порядка байтов, другой потенциальной проблемой переносимости является размер короткого и длинного. ntohl прочитает 4 байта и преобразует их в 32-разрядное целое число. Следовательно, целевое значение int должно быть не менее 32 бит, чтобы удерживать его, оно не должно быть точно такой длины. ntohs читает 2 байта и преобразует в короткое целое число из 16 бит. Обратите внимание, что если ваша собственная платформа использует более 32 бит для длинных или 16 бит для коротких, вы должны решить проблему со знаком, если они являются целыми числами со знаком (потому что фактический тип для ntohl - без знака).

Поскольку все больше машин, в том числе и Linux, используют процессоры Intel с порядковым порядком байтов, гораздо чаще теперь вы используете этот формат в качестве формата «по умолчанию» и получаете изменения в формате с прямым порядком байтов. В таком случае вы можете захотеть написать собственные макросы для преобразования в формат с прямым порядком байтов (на платформе с прямым порядком байтов они будут недоступны).

Кстати, для реверсирования байтов вы можете использовать std :: reverse, и вам понадобятся два указателя, один из которых указывает на первый байт, а другой - на один после последнего байта.

Вы также можете реализовать «байт-своп», и тогда ваш правый указатель должен быть на последнем байте, а не на одном конце. Вы byteswap, как это:

void byteswap( unsigned char & byte1, unsigned char & byte2 )
{
   byte1 ^= byte2;
   byte2 ^= byte1;
   byte1 ^= byte2;
}

Для реализации в C (а не в C ++) вы бы использовали указатель, а не ссылку в качестве параметра.

В приведенном вами примере, похоже, файл хранится в 32-битном порядке байтов с прямым порядком байтов (то есть в сети) в соответствии со своей спецификацией, поэтому вы можете просто использовать ntohl, однако ntohl принимает беззнаковое целое как параметр. Таким образом, исправьте ваш код следующим образом:

uint32_t count = 0;
myfile.open ("truss.rst", ios::binary);
myfile.read(reinterpret_cast<char*>(&count), sizeof(uint32_t)); 
   // ideally validate that the read succeeded
count = ntohl( count );

Одна из слабостей в iostream, по моему мнению, что вы должны сделать это. Тот, кто написал это, никогда не любил концепцию двоичного ввода / вывода. Конечно, если вы пишете это на C, а не на C ++, вы должны использовать FILE* и fread.

...