Данная мини-статья родилась в связи с тем, что мне понадобилось читать/писать двоичный файл. Соглашусь, задача для Руби несколько нетипичная, но надо - значит надо.
В сети информации по данной теме практически нет. Что-то есть на google groups, но толкового описания я не нашёл. В результате, начал выдумывать сам.
Итак была задача научиться читать и писать двоичный файл, созданный с помощью MFC метода Serialize. В файле присутствуют CString, int, float, double, BOOL и COLORREF.
Приведу кусочек кода на C++:
// создание файла
... ::Serialize(CArchive& archive)
{
int A = 44;
COLORREF Color = RGB(255,10,128);
float F = 0.345678;
BOOL B = FALSE;
CString str = _T("This is a sample string.");
archive << A << Color << F << B << str;
}
... ::Serialize(CArchive& archive)
{
int A = 44;
COLORREF Color = RGB(255,10,128);
float F = 0.345678;
BOOL B = FALSE;
CString str = _T("This is a sample string.");
archive << A << Color << F << B << str;
}
Небольшое отступление про сериализацию в MFC:
данные в файле хранятся "как есть", за исключением CString. Сей объект хранится в паскалевской нотации вида <1 байт - длина строки><строка>. В случае строк длиннее 255 символов - в виде <0xFF><2 байта - длина строки><строка>. В случае нулевой строки - <0>.
данные в файле хранятся "как есть", за исключением CString. Сей объект хранится в паскалевской нотации вида <1 байт - длина строки><строка>. В случае строк длиннее 255 символов - в виде <0xFF><2 байта - длина строки><строка>. В случае нулевой строки - <0>.
Речь идёт про ANSI строки, как хранятся UNICODE строки я не выяснял, в мои задачи это не входило. Предлагаю вам самим исследовать этот вопрос и поделиться с народом :)
Далее я применил слегка "нечестный" метод - подсчитал сколько байт занимает тот или иной тип. "Нечестный" - потому что на разных платформах данные будут разными. Меня интересовал Windows XP и 32-х битный компилятор Visual Studio 7.1.
#include ...
void main(void)
{
printf("float:%d, double:%d, int:%d, COLORREF:%d, BOOL:%d\n",
sizeof(float), sizeof(double), sizeof(int), sizeof(COLORREF), sizeof(BOOL));
}
void main(void)
{
printf("float:%d, double:%d, int:%d, COLORREF:%d, BOOL:%d\n",
sizeof(float), sizeof(double), sizeof(int), sizeof(COLORREF), sizeof(BOOL));
}
Получаем для Windows XP:
float:4, double:8, int:4, COLORREF:4, BOOL:4
Собственно и всё. Далее - дописываем нужные методы к классу "File":
class File
def read_float
f = read(4).unpack("f")
end
def read_float
f = read(4).unpack("f")
end
def read_int
f = read(4).unpack("i")
end
f = read(4).unpack("i")
end
def read_cstring
f = read(1).unpack("c")
if (f == 0)
return ""
end
if (f[0] == -1)
f = read(2).unpack('s')
str = read(f[0])
return str
end
str = read(f[0].to_i)
end
f = read(1).unpack("c")
if (f == 0)
return ""
end
if (f[0] == -1)
f = read(2).unpack('s')
str = read(f[0])
return str
end
str = read(f[0].to_i)
end
def read_colorref
f = read(4).unpack("i")
end
def read_double
f = read(8).unpack("d")
end
f = read(4).unpack("i")
end
def read_double
f = read(8).unpack("d")
end
def write_float(float_var)
write(float_var.pack("4f"))
end
write(float_var.pack("4f"))
end
def write_double(double_var)
write(double_var.pack("8d"))
end
write(double_var.pack("8d"))
end
def write_int(int_var)
write(int_var.pack("4i"))
end
write(int_var.pack("4i"))
end
def write_colorref(colorref_var)
write(colorref_var.pack("4i"))
end
write(colorref_var.pack("4i"))
end
# TODO Поддержать длинные строки
def write_cstring(str_var)
len = str_var.length
write([len].pack("1c"))
write(str_var) if len > 0
end
end
def write_cstring(str_var)
len = str_var.length
write([len].pack("1c"))
write(str_var) if len > 0
end
end
Читаем:
def ReadBinary
f = File.open ("our.binary.file", "rb")
@A = f.read_int
@Color = f.read_colorref
@F = f.read_float
@B = f.read_int
@str = f.read_cstring
f.close
end
f = File.open ("our.binary.file", "rb")
@A = f.read_int
@Color = f.read_colorref
@F = f.read_float
@B = f.read_int
@str = f.read_cstring
f.close
end
Пишем:
def WriteBinary
f = File.open("our.binary.file", "wb")
f.write_int(@A)
f.write_colorref(@Color)
f.write_float(@F)
f.write_int (@B)
f.write_cstring(@str)
f.close
end
f = File.open("our.binary.file", "wb")
f.write_int(@A)
f.write_colorref(@Color)
f.write_float(@F)
f.write_int (@B)
f.write_cstring(@str)
f.close
end
Разумеется, порядок чтения/записи должен соответствовать порядку в функции сериализации.
Комментариев нет:
Отправить комментарий