Записывать необработанную двоичную структуру в файл в D?

Aug 19 2020

Я пытаюсь создать двоичный файл, содержащий несколько двоичных записей, определенных в некоторых struct. Однако я не могу найти, как это сделать. Посмотрев на другие примеры, мне удалось написать strings без проблем, но нет struct. Я просто хочу написать его, как в случае Cс fwrite(3), но в Dверсии 2.

Вот что я пробовал до сих пор:

  • using stream.write(tr)- записывает удобочитаемое / отладочное представление
  • using stream.rawWrite(tr)- это звучало как то, что мне нужно, но не компилируется с помощью:

Ошибка: шаблон std.stdio.File.rawWrite не может выводить функцию из типов аргументов! () (TitleRecord), кандидатами являются:

/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(1132): std.stdio.File.rawWrite (T) (в буфере T [])

  • пытаясь, rawWriteкак указано выше, но приводя данные к разным вещам, также никогда не компилируется.
  • даже пытается вернуться к Cс fwrite, но не может получить достаточно глубоко , чтобы получить дескриптор файла I необходимость.

Чтение документации не очень помогло (мне тоже подходит написание строк, но не написание struct). Я уверен, что должен быть простой способ сделать это, но я не могу его найти .... Другие ТАК вопросы мне не помогли . Я D 1.0, возможно, это было бы сделано, stream.writeExact(&tr, tr.sizeof)но это уже не вариант.

import std.stdio;

struct TitleRecord {
        short id;
        char[49] text;
};

TitleRecord tr;

void main()
{
 auto stream = File("filename.dat","wb+");
 tr.id = 1234;
 tr.text = "hello world";
 writeln(tr);
 //stream.write(tr);
 //stream.rawWrite(tr);
 //stream.rawWrite(cast(ubyte[52]) tr);
 //stream.rawWrite(cast(ubyte[]) tr);
 //fwrite(&tr, 4, 1, stream);
}

Ответы

2 AdamD.Ruppe Aug 19 2020 at 21:40

Для этого эта ошибка говорит, что ожидает массив, а не структуру. Один из простых способов сделать это - просто нарезать указатель и передать его rawWrite:

stream.rawWrite((&tr)[0 .. 1]);

(&tr)Получает адрес, превращая ваш - структуру на указатель. Затем [0 .. 1]средства получают кусочек этого с самого начала, захватывая только один элемент.

Таким образом, теперь у вас есть объект, T[]который rawWrite может обрабатывать, содержащий ваш единственный элемент.

Имейте в виду, что если вы используете @safeаннотацию, это не пройдет, вам придется ее отметить @trusted. Также, конечно, любые ссылки внутри вашей структуры (в том числе string) будут записаны как двоичные указатели вместо данных, как вы наверняка знаете из опыта C. Но в том случае, если вы там показали, все в порядке.

edit: Кстати, вы также можете просто использовать, fwriteесли хотите, скопировать / вставить тот же код из C (за исключением того, что это foo.sizeofвместо sizeof foo). Элемент D File- это просто небольшая оболочка вокруг C, FILE*и вы можете получить исходный ФАЙЛ * обратно, чтобы передать его другим функциям с помощьюstream.getFP() http://dpldocs.info/experimental-docs/std.stdio.File.getFP.html )

1 LászlóSzerémi Aug 19 2020 at 23:55

rawWrite ожидает массив, но есть много обходных путей.

Один из них - создать массив из одного элемента.

file.rawWrite([myStruct]);

Другой преобразует структуру в массив. В моей библиотеке bitleveld есть функция для этого reinterpretAsArray. Это также упрощает создание контрольных сумм указанных структур.

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