Считайте двоичный файл в структуру

Хотя этот популярный ответ даст вам желаемый синтаксис индексации, он вдвойне неэффективен: большой и медленный как в пространстве, так и во времени.

Почему этот ответ большой и медленный

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

int val = matrix[ x ][ y ];

работает, потому что матрица [x] возвращает указатель на массив, который затем индексируется с помощью [y]. Разрушение:

int* row = matrix[ x ];
int  val = row[ y ];

Удобно, да? Нам нравится наш синтаксис [x] [y].

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

Почему?

Причина, по которой это как жир, так и медленный, на самом деле то же самое. Каждая «строка» в матрице представляет собой отдельно распределенный динамический массив. Создание распределения кучи является дорогостоящим как во времени, так и в пространстве. Распределитель занимает время, чтобы сделать выделение, иногда выполняя алгоритмы O (n), чтобы сделать это. И распределитель «прокладывает» каждый из ваших массивов строк с дополнительными байтами для бухгалтерии и выравнивания. Это дополнительное пространство стоит ... ну ... дополнительное пространство. Освобождение будет также взять дополнительное время, когда вы идете на освобождение матрицы, тщательно освобождая каждое выделение каждой строки. Получает меня в поту, просто думая об этом.

Есть еще одна причина, по которой это медленно. Эти отдельные распределения, как правило, живут в прерывистых частях памяти. Одна строка может быть по адресу 1000, другая по адресу 100 000 - вы получите эту идею. Это означает, что когда вы проходите матрицу, вы прыгаете через память, как дикий человек. Это, как правило, приводит к промахам в кеше, которые значительно замедляют время обработки.

Итак, если вы абсолютный должен иметь свой симпатичный синтаксис [x] [y], используйте это решение. Если вы хотите быстроту и малость (и если вам все равно, почему вы работаете на C ++?), Вам нужно другое решение.

Другое решение

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

class Matrix
{
    ...
    size_t index( int x, int y ) const { return x + m_width * y; }
};

Учитывая эту функцию index() (которую я представляю, является членом класса, потому что он должен знать m_width вашего матрица), вы можете получить доступ к ячейкам в матричном массиве. Матричный массив выделяется следующим образом:

array = new int[ width * height ];

Таким образом, эквивалент этого в медленном, жирном решении:

array[ x ][ y ]

... это в быстрой, малой Решение:

array[ index( x, y )]

Грустный, я знаю. Но вы привыкнете к этому. И ваш процессор поблагодарит вас.

52
задан EM-Creations 21 December 2017 в 04:57
поделиться

6 ответов

Проблема строка с в Вашей структуре. Я нашел, что маршалинг типов как байт/закорачивать/интервал не является проблемой; но когда необходимо упорядочить в составной тип, такой как строка, Вам нужна Ваша структура для явной имитации неуправляемого типа. Можно сделать это с MarshalAs attrib.

Для Вашего примера, следующее должно работать:

[StructLayout(LayoutKind.Explicit)]
struct StructType
{
    [FieldOffset(0)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    public string FileDate;

    [FieldOffset(8)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    public string FileTime;

    [FieldOffset(16)]
    public int Id1;

    [FieldOffset(20)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 66)] //Or however long Id2 is.
    public string Id2;
}
29
ответ дан Ishmaeel 7 November 2019 в 09:34
поделиться

У меня не было удачи с помощью BinaryFormatter, я предполагаю, что у меня должна быть полная структура, которая соответствует содержанию файла точно. Я понял, что в конце не интересовался многим содержания файла так или иначе, таким образом, я пошел с решением чтения части потока в bytebuffer и затем преобразование его с помощью

Encoding.ASCII.GetString()

для строк и

BitConverter.ToInt32()

для целых чисел.

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

3
ответ дан thkala 7 November 2019 в 09:34
поделиться

Я не вижу проблемы с Вашим кодом.

только из моей головы, что, при попытке сделать это вручную? это работает?

BinaryReader reader = new BinaryReader(stream);
StructType o = new StructType();
o.FileDate = Encoding.ASCII.GetString(reader.ReadBytes(8));
o.FileTime = Encoding.ASCII.GetString(reader.ReadBytes(8));
...
...
...

также попытка

StructType o = new StructType();
byte[] buffer = new byte[Marshal.SizeOf(typeof(StructType))];
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
Marshal.StructureToPtr(o, handle.AddrOfPinnedObject(), false);
handle.Free();

тогда использование буфер [] в Вашем BinaryReader вместо того, чтобы считать данные с FileStream, чтобы видеть, получаете ли Вы все еще исключение AccessViolation.

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

, Который имеет смысл, BinaryFormatter имеет свой собственный формат данных, абсолютно несовместимый с Вашим.

2
ответ дан lubos hasko 7 November 2019 в 09:34
поделиться

Чтение прямо в структуры является злым - много программ C упали из-за различных порядков байтов, различных реализаций компилятора полей, упаковки, размера слова.......

Вы являетесь лучшими из сериализации и десериализации байта байтом. Используйте сборку в материале, если Вы хотите или просто привыкаете к BinaryReader.

0
ответ дан Ronnie 7 November 2019 в 09:34
поделиться

Попробуйте это:

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
0
ответ дан urini 7 November 2019 в 09:34
поделиться

Как сказал Ронни, я бы использовал BinaryReader и прочитал каждое поле отдельно. Я не могу найти ссылку на статью с этой информацией, но было замечено, что использование BinaryReader для чтения каждого отдельного поля может быть быстрее, чем Marshal.PtrToStruct, если структура содержит менее 30-40 полей или около того. Ссылку на статью выложу, когда найду.

Ссылка на статью находится по адресу: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

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

6
ответ дан 7 November 2019 в 09:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: