Есть ли BinaryReader на С++ для чтения данных, записанных с BinaryWriter на С#?

Я написал несколько int, char[] и тому подобное в файл данных с помощью BinaryWriter на C#. Прочитав файл обратно (на C#) с помощью BinaryReader, я могу идеально воссоздать все части файла.

Однако попытка прочитать их обратно с помощью C++ приводит к пугающим результатам. Я использовал fstream, чтобы попытаться прочитать данные, и данные не считывались правильно. В C++ я настроил fstream с помощью ios::in|ios::binary|ios::ate и использовал seekg для определения своего местоположения. Затем я прочитал следующие четыре байта, которые были записаны как целое число «16» (и правильно читаются в C#). На С++ это читается как 1244780 (я проверял, а не адрес памяти). С чего бы это? Есть ли эквивалент BinaryReader в C++? Я заметил, что это упоминается в msdn, но это Visual C++, а intellisense для меня даже не похож на C++.

Пример кода для записи файла (C#):

    public static void OpenFile(string filename)
    {
        fs = new FileStream(filename, FileMode.Create);
        w = new BinaryWriter(fs);

    }

    public static void WriteHeader()
    {
        w.Write('A');
        w.Write('B');
    }

    public static byte[] RawSerialize(object structure)
    {
        Int32 size = Marshal.SizeOf(structure);
        IntPtr buffer = Marshal.AllocHGlobal(size);
        Marshal.StructureToPtr(structure, buffer, true);
        byte[] data = new byte[size];
        Marshal.Copy(buffer, data, 0, size);
        Marshal.FreeHGlobal(buffer);
        return data;
    }

    public static void WriteToFile(Structures.SomeData data)
    {
        byte[] buffer = Serializer.RawSerialize(data);
        w.Write(buffer);
    }

Я не уверен, как я мог бы показать вам файл данных.

Пример обратного чтения данных (C#):

        BinaryReader reader = new BinaryReader(new FileStream("C://chris.dat", FileMode.Open));
        char[] a = new char[2];
        a = reader.ReadChars(2);
        Int32 numberoffiles;
        numberoffiles = reader.ReadInt32();
        Console.Write("Reading: ");
        Console.WriteLine(a);
        Console.Write("NumberOfFiles: ");
        Console.WriteLine(numberoffiles);

Это я хочу выполнить на С++. Начальная попытка (сбой при первом целом):

 fstream fin("C://datafile.dat", ios::in|ios::binary|ios::ate);
 char *memblock = 0;
 int size;
 size = 0;
 if (fin.is_open())
 {
  size = static_cast<int>(fin.tellg());
  memblock = new char[static_cast<int>(size+1)];
  memset(memblock, 0, static_cast<int>(size + 1));

  fin.seekg(0, ios::beg);
  fin.read(memblock, size);
  fin.close();
  if(!strncmp("AB", memblock, 2)){ 
   printf("test. This works."); 
  }
  fin.seekg(2); //read the stream starting from after the second byte.
  int i;
  fin >> i;

Редактировать: кажется, что независимо от того, в каком месте я использую «seekg», я получаю точно такое же значение.


person Chris    schedule 06.10.2009    source источник
comment
Можете ли вы показать нам фрагмент кода (или весь код) и пример двоичного файла?   -  person coelhudo    schedule 06.10.2009
comment
Я разместил код. Не уверен, куда я могу загрузить двоичный файл.   -  person Chris    schedule 06.10.2009
comment
вы читаете chris.dat в своем читателе С# и datafile.dat в своем читателе С++...   -  person Pondidum    schedule 06.10.2009
comment
@ Энди, расхождения в именах просто из-за моего тестирования туда и обратно.   -  person Chris    schedule 06.10.2009
comment
Попробуйте написать только одно целое число, чтобы не беспокоиться о размерах символов. Запишите его, посмотрите, сможете ли вы его прочитать, и сообщите, как файл выглядит в шестнадцатеричном редакторе.   -  person Bill    schedule 06.10.2009


Ответы (4)


Вы понимаете, что char имеет 16 бит в C#, а не 8, как обычно в C. Это связано с тем, что char в C# предназначен для обработки текста Unicode, а не необработанных данных. Таким образом, запись символов с помощью BinaryWriter приведет к записи Unicode, а не необработанных байтов.

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

EDIT1
Что касается вашего кода C++, не используйте оператор >> для чтения из двоичного потока. Используйте read() с адресом int, который вы хотите прочитать.

int i;
fin.read((char*)&i, sizeof(int));

EDIT2
Чтение из закрытого потока также может привести к неопределенному поведению. Вы не можете вызвать fin.close(), а затем ожидать, что сможете прочитать его.

person Yacoby    schedule 06.10.2009
comment
Символ c/c++ может обрабатывать unicode в виде строки utf-8. - person anno; 02.02.2010

Это может быть или не быть связано с проблемой, но...

Когда вы создаете BinaryWriter, он по умолчанию записывает chars в UTF-8. Это означает, что некоторые из них могут быть длиннее одного байта, что мешает поиску.

Этого можно избежать, используя конструктор с двумя аргументами для указания кодировки. Экземпляр System.Text.ASCIIEncoding будет таким же, как и C/C++ используется по умолчанию.

person Powerlord    schedule 06.10.2009
comment
Проблема с ASCIIEncoding заключается в том, что он незаметно искажает символы, отличные от ASCII. - person CodesInChaos; 03.12.2014
comment
Вы НИКОГДА не должны использовать строковый тип С# для такого взаимодействия. Если вы не знаете, что делаете, что сейчас не так. Используйте байт[]. Даже продвинутые программисты вроде меня боятся типа String. Используйте варианты кодирования для преобразования строки в байтовый массив, затем запишите ее размер и данные. Прочитайте его обратно на С++, используя подход Якоби и некоторую библиотеку Unicode, например icu. - person Петър Петров; 23.11.2016

В вашем фрагменте C++ много чего не так. Вы не должны смешивать двоичное чтение с форматированным чтением:

  // The file is closed after this line. It is WRONG to read from a closed file.
  fin.close();

  if(!strncmp("AB", memblock, 2)){ 
   printf("test. This works."); 
  }

  fin.seekg(2); // You are moving the "get pointer" of a closed file
  int i;

  // Even if the file is opened, you should not mix formatted reading
  // with binary reading. ">>" is just an operator for reading formatted data.
  // In other words, it is for reading "text" and converting it to a 
  // variable of a specific data type.
  fin >> i;
person AraK    schedule 06.10.2009
comment
Большое спасибо. Я давно не работал с такими вещами и нуждаюсь в этом :) - person Chris; 06.10.2009

Если это поможет, я рассмотрел, как BinaryWriter записывает данные здесь.

Прошло некоторое время, но я процитирую его и надеюсь, что это точно:

  • Int16 записывается как 2 байта и дополняется.
  • Int32 записывается как Little Endian и дополняется нулями.
  • С числами с плавающей запятой сложнее: он берет значение с плавающей запятой и разыменовывает его, получая содержимое адреса памяти, которое является шестнадцатеричным
person Chris S    schedule 06.10.2009
comment
Может ли int32 быть прямым порядком байтов и дополнен 0, может ли это вызывать некоторые проблемы? Можно вообще поподробнее? (Извините, ссылку еще не проверял. Возможно, там есть более подробная информация) - person Chris; 06.10.2009
comment
Похоже, это было связано с символом С++ и не имело ничего общего с целыми числами, за исключением смещения - person Chris S; 06.10.2009
comment
Страница 404 не найдена. - person Changming Sun; 10.10.2015
comment
@ChangmingSun, спасибо, я обновил ссылку, если бы вы могли отменить отрицательный голос - person Chris S; 12.10.2015
comment
@ChrisS, твой ответ напугает его. Int16 и Int32, как и числа с плавающей запятой, непосредственно преобразовываются, поскольку являются нативными типами. BinaryWriter записывает их как есть, а memcpy() объединяет их в соответствующих типах C++: int16_t, int32_t и float — правильный способ. - person Петър Петров; 23.11.2016