Вопрос кроссплатформенного программирования (файловый ввод / вывод) - PullRequest
1 голос
/ 28 октября 2009

У меня есть класс C ++, который выглядит примерно так:

class BinaryStream : private std::iostream
{
    public:
        explicit BinaryStream(const std::string& file_name);
        bool read();
        bool write();

    private:
        Header m_hdr;
        std::vector<Row> m_rows;        
}

Этот класс читает и записывает данные в двоичном формате на диск. Я не использую какую-либо платформо-зависимую кодировку - вместо этого полагаюсь на STL. Я успешно скомпилировал на XP. Мне интересно, могу ли я FTP файлы, написанные на платформе XP, и читать их на своем компьютере с Linux (как только я перекомпилирую библиотеку двоичных потоков в Linux).

Описание:

  1. Файлы, созданные на компьютере Xp с использованием кроссплатформенной библиотеки, скомпилированной для XP.
  2. Скомпилируйте ту же библиотеку (использованную в 1 выше) на компьютере с Linux

Вопрос: Можно ли читать файлы, созданные в 1 выше, на компьютере с Linux (2)?

Если нет, объясните, почему нет, и как я могу обойти эту проблему.

Ответы [ 7 ]

1 голос
/ 28 октября 2009

Вот статья Endianness , связанная с вашим вопросом. Ищите «Порядковый номер в файлах и обмен байтов». Вкратце, если на вашем Linux-компьютере те же порядковые номера, что и в норме, если нет - могут возникнуть проблемы.

Например, когда в XP записано целое число 1 в файле, оно выглядит так: 10 00

Но когда целое число 1 записывается в файл на компьютере с другим порядком байтов, оно будет выглядеть так: 00 01

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

1 голос
/ 28 октября 2009

Если вы хотите, чтобы ваш код переносился на машины с различным порядком байтов, вам нужно придерживаться одного порядка байтов в ваших файлах. Всякий раз, когда вы читаете или записываете файлы, вы делаете преобразования между порядком байтов хоста и порядком байтов файла. Обычно вы используете то, что вы называете сетевым порядком байтов, когда вы хотите записать файлы, переносимые на все машины. Порядок байтов в сети определен с прямым порядком байтов, и есть готовые функции, предназначенные для этих преобразований (хотя их очень легко написать самостоятельно).

Например, перед записью long в файл вы должны преобразовать его в сетевой порядок байтов с помощью htonl (), а при чтении из файла вы должны преобразовать его обратно в байтовый порядок хоста с помощью ntohl (). В системе с прямым порядком байтов htonl () и ntohl () просто возвращают то же число, что было передано в функцию, но в системе с прямым порядком байтов он меняет каждый байт в переменной.

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

Еще одна важная вещь, на которую следует обратить внимание - это заполнение ваших структур / классов, которые вы пишете, если вы записываете их непосредственно в файл (например, Header и Row). Разные компиляторы на разных платформах могут использовать разные отступы, что означает, что переменные выровнены по-разному в памяти. Это может сильно испортить ситуацию, если компиляторы, которые вы используете на разных платформах, используют разные отступы. Поэтому для структур, которые вы намереваетесь записывать непосредственно в файлы / другие потоки, вы всегда должны указывать заполнение. Вы должны указать компилятору упаковать ваши структуры следующим образом:

#pragma pack(push, 1)
struct Header {
  // This struct uses 1-byte padding
  ...
};
#pragma pack(pop)

Помните, что это сделает использование структуры более неэффективным, когда вы используете ее в своем приложении, потому что доступ к невыровненным адресам памяти означает больше работы для системы. Вот почему, как правило, неплохо иметь отдельные типы для упакованных структур, которые вы записываете в потоки, и тип, который вы фактически используете в приложении (вы просто копируете элементы из одного в другой).

EDIT. Конечно, еще один способ решить эту проблему - это самостоятельно сериализовать эти структуры, для которых не требуется использование #pragma (прагмы зависят от компилятора, хотя все основные компиляторы, насколько мне известно, поддерживают пакет прагмы).

1 голос
/ 28 октября 2009

Производная от std::basic_streambuf. Вот для чего они здесь. Обратите внимание, что большинство классов STL не предназначены для использования в. Тот, который я упоминаю, является исключением.

1 голос
/ 28 октября 2009

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

Вы также можете столкнуться с проблемами, связанными с символом конца строки. Здесь недостаточно информации о том, как вы используете ::std::iostream, чтобы дать вам хороший ответ на этот вопрос.

Я бы настоятельно рекомендовал взглянуть на библиотеку protobuf . Это отличная библиотека для создания быстрых кроссплатформенных двоичных кодировок.

0 голосов
/ 28 октября 2009

Если вы записываете struct / class прямо на диск, то не надо.

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

Из приведенного выше кода не ясно, что вы на самом деле пишете в файл.

0 голосов
/ 28 октября 2009

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

0 голосов
/ 28 октября 2009

Пока это простые двоичные файлы, они должны работать

...