Translate

Чтение/запись двоичных файлов на Ruby.

Данная мини-статья родилась в связи с тем, что мне понадобилось читать/писать двоичный файл. Соглашусь, задача для Руби несколько нетипичная, но надо - значит надо.
В сети информации по данной теме практически нет. Что-то есть на 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;
}
Небольшое отступление про сериализацию в MFC:
данные в файле хранятся "как есть", за исключением 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));
}
Получаем для 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_int
        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
    def read_colorref
          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
    def write_double(double_var)
        write(double_var.pack("8d"))
    end
    def write_int(int_var)
        write(int_var.pack("4i"))
    end
    def write_colorref(colorref_var)
        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 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
Пишем:
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
Разумеется, порядок чтения/записи должен соответствовать порядку в функции сериализации.

Комментариев нет:

Отправить комментарий

Постоянные читатели