Как перевернуть данные, используя массив указателей (парсинг двоичного файла) - PullRequest
0 голосов
/ 27 июня 2010

Я анализирую двоичный файл, используя спецификацию.Файл поступает в режиме с прямым порядком байтов, поскольку в нем скопились потоковые пакеты.Я должен обратить длину пакетов, чтобы «переинтерпретировать» их в правильный тип переменной.(Я не могу использовать функцию net / inet.h, потому что пакеты имеют разную длину).

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

Если кто-то знает более эффективный способ сделать этоПожалуйста, дайте мне знать (8 ГБ данных должны быть проанализированы).

#include <iostream>
#include <fstream>

void reverse(char &array[]);

using namespace std;

int main ()
{
    char *a[5];
    *a[0]='a'; *a[1]='b'; *a[2]='c'; *a[3]='d'; *a[4]='e';

    reverse(a);

    int i=0;
    while(i<=4)
    {
        cout << *a[i] << endl;
        i++;
    }
    return 0;
}
void reverse(char &array[])
{
    int size = sizeof(array[])+1;
    //int size = 5;
    cout << "ARRAY SIZE: " << size << endl;

    char aux;
    for (int i=0;i<size/2;i++)
    {
            aux=array[i];
            array[i]=array[size-i-1];
            array[size-i-1]=aux;
    }
}

Спасибо всем за помощь!

Ответы [ 2 ]

2 голосов
/ 27 июня 2010

Не совсем.

Файл поступает в режиме с прямым порядком байтов, поскольку в нем скопились потоковые пакеты. Я должен обратить длину пакетов, чтобы "переинтерпретировать" их в правильный тип переменной.

Вам нужно обратить байты на уровне хранимых данных, а не файла и не пакетов.

Например, если файл хранит структуру.

struct S {
  int i;
  double d;
  char c;
};

чтобы прочитать структуру, вам нужно будет обратить вспять:

int: [4321]->[1234]  // sizeof(int) == 4, swap the order of 4 bytes
double: [87654321]->[12345678]  // sizeof(double) == 8, swap the order of 8 bytes
char: [1]->[1]  // sizeof(char) == 1, swap 1 byte (no swapping needed)

Не вся структура сразу.

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

Функции в inet.h используются именно для этой цели, поэтому я рекомендую вам их использовать.

Итак, это приводит нас к строкам. Если вы храните строки c в файле, вам нужно поменять их местами? Ну, а строка c - это последовательность из 1 байта char с. Вам не нужно менять 1 байт char с, поэтому вам не нужно менять данные в строке c!

Если вы действительно хотите поменять местами 6 байтов, вы можете использовать функцию std::reverse:

char in[6] = get6bytes();
cout << in << endl;  // shows abcdef 
std::reverse(in, in+6);
cout << in << endl;  // shows fedcba

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

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

0 голосов
/ 27 июня 2010

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

Я думаю эта статья должна помочь вам, а также этот вопрос для SO , он показываеткак реализовывать преобразования различными способами, причем самая быстрая - побитовая реализация.Он не показывает реализации для поля шириной шесть байт, но аналогичное решение может быть легко сделано.

Я предлагаю скопировать поле длины в 64-битное целое число, а затем реализовать пользовательскую функцию для замены соответствующих 6 байтов.Избавьтесь или все указатели на символы в любом случае ...;)

Если вы компилируете на VC ++, есть эта функция: _byteswap_uint64 .Пройдите свои 6 байтов в верхнем конце этого uint64, вызовите эту функцию и сделайте гопла, все готово.

редактирование в 4:12 (я должен быть очень зависимым от переполнения стека)

#include <iostream>
#include <stdlib.h>

typedef unsigned char    byte;
typedef unsigned __int64 uint64_t; // uncomment if you are not on VC++

// in case you are not compiling with VC++ use this custom function
// It can swap data of any size. Adapted from:
// https://stackoverflow.com/questions/2182002/convert-big-endian-to-little-endian-in-c-without-using-provided-func/2182581#2182581
// see: http://en.wikipedia.org/wiki/XOR_swap_algorithm

void
swapBytes( void* v, size_t n )
{
   byte* in = (byte*) v;

   for( size_t lo=0, hi=n-1; hi>lo; ++lo, --hi )

      in[lo] ^= in[hi]
   ,  in[hi] ^= in[lo]
   ,  in[lo] ^= in[hi] ;
}

#define SWAP(x) swapBytes( &x, sizeof(x) );


int
main()
{
   // pointer to location of length field. 
   // You will have to read it from file to memory.
   byte length[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };

   // ok, you have read it from file, now get it in an uint64_t
   uint64_t i = *( (uint64_t*)  length );

   i <<= 16; // zero two bytes and move everything to the high end.

   std::cout << std::hex << i                     << std::endl;
   std::cout << std::hex << _byteswap_uint64( i ) << std::endl;

   // generic swapping function
   SWAP( i ) 
   std::cout << std::hex << i                     << std::endl;

   std::cin.get();
   return 0;
}

// Outputs:
// 605040302010000
// 10203040506
// 10203040506
...