Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Массив структур в файл


Автор: LordNet 27.9.2005, 21:11
Люди посоветуйти как лутчи произвести ввод/вывод массива структур в файл и из файла?
Зарание спасабо!!!

Автор: azesmcar 28.9.2005, 08:21
Что то вроде этого.
Код

#include <stdio.h>

typedef struct {
  int a;
  char k;
  double v;
} my_struct;
     
void write_to_file(const char *pFileName, my_struct* slist, int nSize)
{
  FILE *fp = fopen( "structs.dat", "w" );
  fwrite( slist, nSize, sizeof( my_struct ), fp );
  fclose( fp );
}

void read_from_file( const char *pFileName, my_struct* slist, int nSize )
{
  FILE *fp = fopen( "structs.dat", "r" );
  fread( slist, nSize, sizeof( my_struct ), fp );
  fclose( fp );
}

int main()
{
  my_struct slist[10];
  write_to_file( "struct.dat", slist, 10 );
  read_from_file( "struct.dat", sl, 10 );
  return 0;
}


Автор: Denn 28.9.2005, 09:40
если в структурах есть указатели, не так все просто.

Автор: azesmcar 28.9.2005, 09:42
Цитата
если в структурах есть указатели, не так все просто.


А кто говорил про указатели?

Автор: Denn 28.9.2005, 12:30
это на тот случай, если у автора есть в структурах указатели, но он их не упамянул.

Автор: azesmcar 28.9.2005, 12:35
Цитата
это на тот случай, если у автора есть в структурах указатели, но он их не упамянул.


В принципе если есть указатели тогда фиг так легко отделаешся...как минимум описание структуры должно быть...универсальную функцию для этого написать будет сложновато...я бы даже сказал очень сложновато...
Добавлено @ 12:37
Надо будет пройтись по структуре, выяснить где у нее находятся динамические переменные, а где статические...а потом еще проверить а может динамическая переменная не инициализирована? а вдруг она уже удалена и программа полетит к чертовой матери...

Это не на пару строк тема...
Добавлено @ 12:40
Пока не представляю как можно выяснить что из себя представляет кусок памяти размером в 4 байта, переменную типа int или указатель...но задача интересная, можно на ней подумать...

Автор: Mayk 28.9.2005, 12:59
Цитата(azesmcar @ 28.9.2005, 16:35)

Пока не представляю как можно выяснить что из себя представляет кусок памяти размером в 4 байта, переменную типа int или указатель...но задача интересная, можно на ней подумать...

Не стоит. Элджер довольно не плохо обдумывал подобное в С++ Библиотеке программиста(оригназв "C++ for real programmers"). Что-либо утешительного Элджер не надумал.

Автор: azesmcar 28.9.2005, 15:00
Ну почему же? можно поизвращяться...

Передать функции write_struct_to_file массив описивающий структуру...например

Код


typedef struct {
    int a;
    double t;
    char *k;
} my_struct;

my_struct obj;

obj.a = 5;
obj.t = 2.57;
strcpy( obj.k, "Hello world!!!" );

int struct_description[3][2];
struct_description[0][0] = 4; //размер первого member-а
struct_description[0][1] = 0; //тип первого member-а (0 - static, 1 - pointer)

struct_description[1][0] = 8; //размер второго member-а
struct_description[1][1] = 0; //тип второго member-а (0 - static, 1 - pointer)

struct_description[2][0] = strlen( obj.k ) ; //размер третьего member-а
struct_description[2][1] = 1; //тип третьего member-а (0 - static, 1 - pointer)



Но все равно НЕ ИНТЕРЕСНО smile

Автор: np9mi7 30.9.2005, 01:00
Если в структуре нет указателей, то самый оптимальный (в смысле скорости) способ записи массива (линейного контейнера) - бинарный, через потоки ввода вывода;

Код

    template <class T> ostream& operator << (ostream& refStream, const vector <T>& refVector) throw ()
    {
        refStream.write (reinterpret_cast <const char*> (&refVector.front ()), refVector.size () * sizeof (T));
        //-----------------------------------------------------
        return refStream;
    }


Но тут возникает проблема считывания данных из потока (перед считыванием необходимо знать сколько объектов собираешся считать).

Вообще низкоуровневый вывод/вывод в С++ это не то, что необходимо использовать, используй все преимущества потоков ввода вывода, например:

Определи в некотором базовом классе две функции: bool ToStream (std::ostream&) const и bool FromStream (const std::istream&);, или (что более красиво), одну перегруженную функцию:

Код

struct IObject
{
    virtual bool Serialize (std::ostream&) const
    {
        assert (! "IObject not have implementation for method bool Serialize (std::ostream&) const!");
        //-----------------------------------------------------------
    }
    virtual bool Serialize (const std::istream&)
    {
        assert (! "IObject not have implementation for method bool Serialize (const std::istream&)!");
        //-----------------------------------------------------------
    }
    virtual ~IObject ()
    {
        //-----------------------------------------------------------
    }
};


Потом отнаследуй свои структуры (объекты) от данного интерфейса и реализуй его:
Код

struct Figures : public IObject
{
    float    m_flFloat;
    double    m_dbDouble;
    int        m_iInt;
    //-----------------------------------------------------------------------------------
    explicit Figures (const float flFloat = float (), const double dbDouble = double (), const int iInt = int ()) : IObject (), m_flFloat (flFloat), m_dbDouble (dbDouble), m_iInt (iInt)
    {
        //-----------------------------------------------------------
    }
    //-----------------------------------------------------------------------------------
    virtual bool Serialize (std::ostream& refOstream) const
    {
        refOstream << m_flFloat;
        //-----------------------------------------------------------
        refOstream << m_dbDouble;
        //-----------------------------------------------------------
        refOstream << m_iInt;
        //-----------------------------------------------------------
        return true;
    }
    virtual bool Serialize (const std::istream& refIstream)
    {
        refIstream >> m_flFloat;
        //-----------------------------------------------------------
        refIstream >> m_dbDouble;
        //-----------------------------------------------------------
        refIstream >> m_iInt;
        //-----------------------------------------------------------
        return true;
    }
    //-----------------------------------------------------------------------------------
};
, после всего для "полной совместимости" с потоками ввода/вывода можно добавить пару новых функций (перегруженных операторов)
Код

std::ostream& operator << (std::ostream& refStream, const IObject& refObject)
{
    refObject.Serialize (refStream);
    //---------------------------------------------------------------
    return refStream;
}
const std::istream& operator >> (const std::istream& refStream, IObject& refObject)
{
    refObject.Serialize (refStream);
    //---------------------------------------------------------------
    return refStream;
}

тебе необходимо положить линейный контейнер (массив) в поток (файл). Заполняем линейный контейнер:
Код

std::vector <Figures> vcFigures (5);                                    // в контейнере пять объектов (значения заданы конструктором по умолчанию)
std::ofstream ofStream ("figures.txt");                                                                // файл для вывода
std::copy (vcFigures.begin (), vcFigures.end (), 
    std::ostream_iterator <Figures> (ofStream, " "));                    // собственно запись в файл


При работе с объектами в С++ не используй низкоуровневый ввод/вывод - используй потоки ввода/вывода!
И забудь про массивы в С++, есть ведь линейные контейнеры!

Примечание: методы сериализации специально упрощены, необходимо добавить проверки свойства good () и.т.д.

Автор: Mad 30.9.2005, 01:27
Цитата(np9mi7 @ 30.9.2005, 00:00)
самый оптимальный (в смысле скорости) способ записи массива (линейного контейнера) - бинарный, через потоки ввода вывода

Функции работы с потоками достаточно медленны по выполнению (в сравнении с буферизированным вводом/выводом) smile

Цитата(np9mi7 @ 30.9.2005, 00:00)
И забудь про массивы в С++, есть ведь линейные контейнеры!

опять имеем дело с потерей производительности, простой массив работает гораздо быстрей smile


а вот идея с перегрузкой довольно неплоха (причем тут можно даже с указателями справиться)
получиться примерно так :

Код

typedef struct tagSerializableStruct
{
     virtual void Save(FILE *pFile) = 0;
     virtual void Load(FILE *pFile) = 0;
} SerialazableStruct;

void LoadStructArray(FILE *pFile, SerializableStruct** pDest, int count)
{
    for(int i=0; i<count; i++)
    {
         pDest[i] = new NeedStructType;
         pDest[i]->Load(pFile);
    }
}

void SaveStructArray(FILE *pFile, SerializableStruct** pSrc, int count)
{
    for(int i=0; i<count; i++)
    {
         pDest[i]->Save(pFile);
    }
}


Правдо всеравно придеться определить все нужные типы структур smile так что это полу универсальность
В добавок можно еще перед самой структурой писать в файл ее тип (или по номеру или по имени) а при считывании проверять

Автор: Mayk 30.9.2005, 07:19
Цитата(np9mi7 @ 30.9.2005, 05:00)
  refStream.write (reinterpret_cast <const char*> (&refVector.front ()), refVector.size () * sizeof (T));

юзать sizeof(t) не самая лучшая идея - выравнивание данных на диске нам не к чеме, ведь так? А эти #pragma... Ну вообщем имхо они не красивые.

Цитата(Mad @ 30.9.2005, 05:27)

опять имеем дело с потерей производительности, простой массив работает гораздо быстрей

Я тоже оптимизацию люблю smile
Хотя всё же vector достаточно быстр. Ну всё равно не так, как сишный. В с++ realloc нет smile

Цитата(Mad @ 30.9.2005, 05:27)
pDest[i] = new NeedStructType;
pDest[i]->Load(pFile);

Тогда уж лучше pDest[i] = NeedStructType::load(pFile);
типа виртуальный конструктор.

Цитата(Mad @ 30.9.2005, 05:27)
В добавок можно еще перед самой структурой писать в файл ее тип (или по номеру или по имени) а при считывании проверять

Можно поиграться с хэшированием(хэш ф-ция определяется опытным путем).
Код

#define hashfn(str, salt) (str[0]+salt)+(str[1]?str[1]-salt +...тд  : 0)
struct str{
    enum hash = hashfn(__FILE__, __LINE__);
};

не самый лучший вариант, но тоже вариант smile

Автор: np9mi7 30.9.2005, 08:07
Цитата
опять имеем дело с потерей производительности, простой массив работает гораздо быстрей
, в чем быстрее? Приведи пример.

Цитата
Функции работы с потоками достаточно медленны по выполнению (в сравнении с буферизированным вводом/выводом)
, немного тебя не понял. Приведи пример задачи на которой это реально можно ощутить...

Автор: Mad 30.9.2005, 09:50
np9mi7
По поводу масивов и контейнеров.
Векторы - это динамические массивы и они предназначены для оптимизации использования памяти
Статические массивы - оптимизация производительности
При работе с дин. массивами добавляються дополнительные операции с перераспредилением памяти.

По поводу потоков, если ты посмотриш исходный код с++ библиотек, то увидиш, что внутри реализация сделанна через буфурные функции IO, плюс куча кода smile

Автор: np9mi7 30.9.2005, 11:15
Цитата
По поводу масивов и контейнеров.
Векторы - это динамические массивы и они предназначены для оптимизации использования памяти
Статические массивы - оптимизация производительности
При работе с дин. массивами добавляються дополнительные операции с перераспредилением памяти.
, Mad, это то понятно, ты пример приведи где это ощущается....????

Автор: Mayk 30.9.2005, 12:14
Цитата(np9mi7 @ 30.9.2005, 15:15)
Mad, это то понятно, ты пример приведи где это ощущается....????

Надо полагать там, где быстродействие необходимо.
Взять хотя бы игрушки.


Автор: np9mi7 30.9.2005, 12:43
Mayk, человек утверждает, что:
Цитата
При работе с дин. массивами добавляються дополнительные операции с перераспредилением памяти.
, если ты её не перераспределяешь, то они не добавляются;

Самая частая операция применимая к массиву, это что? Правильно - взятие объекта по индексу:

Код

Figures arrFigures [5];
arrFigures [1];                // одно и тоже
*(arrFigures + 1);            // одно и тоже


Смотрим реализацию этих операций в векторе:
Код

reference operator[](size_type __n) { return *(begin() + __n); }
const_reference operator[](size_type __n) const { return *(begin() + __n); }


Где потеря произволительности?

Автор: azesmcar 30.9.2005, 14:50
Цитата
И забудь про массивы в С++, есть ведь линейные контейнеры!


Ну а ты вроде как утверждаешь что в С++ массивы вообще никому не нужны..всем пора бы про ниx и перейти на STL.. Ты неправ...

Привожу пример, ты решил написать шашки??? так что? чтобы хранить доску ты будешь хранить vector из STL??? есть случаи когда имеет смысл использовать STL а есть случаи когда совсем нет...

хотя бы не стоит использовать потому что ты включаешь в свой проект нехилую библиотеку шаблонных классов...как никак а это бессмысленно...и очень может быть на нашем пентиуме 4 никакой разницы ты даже не почувствуешь, а вот попиши как я годик на процессоре 8xx с тактовой частотой 70 мегагерц и винтом размером с мою оперативную память...сразу почувствуешь...убрав STL из проекта и переписав его на C я в 2 раза скорость работы увеличил...как тебе??? по моему чувствуется
Добавлено @ 14:53
Еще потеря производительности...при использовании массивов в С++ ты пишешь прямо в оперативную память, а в stl за тебя это делает функция...лишний call...еще 50000 таких лишних call-ов и программа начнет работать медленней на несколько секунд...

Зависит от задачи, но советовать забыть массивы С++ это слишком...

Автор: Earnest 30.9.2005, 15:41
Цитата(Mad @ 30.9.2005, 09:50)
При работе с дин. массивами добавляються дополнительные операции с перераспредилением памяти.

Ну, в общем, не совсем это правда - если говорить о хорошей реализации, STL, например.
Если не надо расширять vector, то нет там никаких операций перераспределения памяти. А если надо - что, в C-шном массиве без malloc сможете обойтись? Только все своими ручками придется делать, ошибки плодить, потом отлаживать...
Конечно, теоретически какие-то доп. издержки вроде есть. Но по сравнению с каким-нибудь вызовом Windows API это такие крохи, что под микроскопом не разглядишь. Попробуйте смеха ради заменить где-нибудь в реальном коде массив на vector и посмотреть в профайлере. Не думаю, что увидете разницу раньше, чем на 333 месте по времени...

Цитата(azesmcar @ 30.9.2005, 14:50)
хотя бы не стоит использовать потому что ты включаешь в свой проект нехилую библиотеку шаблонных классов.

Ничего ты не включаешь, кроме того, что используешь.

Цитата(azesmcar @ 30.9.2005, 14:50)
при использовании массивов в С++ ты пишешь прямо в оперативную память, а в stl за тебя это делает функция...лишний call

там практически все inline - посмотри в профайлере .
В том-то и состоит великая мощь шаблонов вообще, и STL в частности, что за потрясающую гибкость ты почти не платишь. "Почти" - потому что увеличивается время компиляции, но и это можно победить с помощью прекомпилированных заголовков.

Автор: azesmcar 30.9.2005, 15:53
Цитата
Ничего ты не включаешь, кроме того, что используешь.


Ну да, а разве я что то другое сказал??? Что-то используешь...что то включаешь...ничего не используешь..ничего не включаешь..логично.


Цитата
там практически все inline - посмотри в профайлере .


Я бы не сказал что все...все что возможно инлайн...а возможно далеко не все...

Я же сказал что конкретно зависит от задачи, стл штука прекрасная, не спорю, но пихать его куда попало все же не стоит...как ни крути а все равно медленней чем с массивами С++...И если использовать СТЛ в конкретной задаче нет смысла то замедлять свою программу даже на какие то 0.000000001 милисекунду не имеет смысла...

Автор: np9mi7 30.9.2005, 16:18
azesmcar, учите мат часть и жизнь сразу станет хороша smile

Автор: azesmcar 30.9.2005, 16:27
А я на жизнь не жалуюсь...учить мне много чего не помешает, как и любому другому...но практика все же показывает что я прав...

Автор: Mayk 30.9.2005, 16:31
В с++ нет realloc()а. Знаю, знаю, operator= и всё такое, но всегда ли оно надо? smile

А вообще, блин, нашли что сравнивать. vector и массив. не народ, вам что - делать нечего?

Автор: Earnest 30.9.2005, 16:31
1) Использование STL ускоряет написание любых программ в разы.

2) Могу представить себе только одну ситуацию, когда нет смысла использовать вектор: если размер массива задан жестко-прежестко константой времени компиляции. И производительность здесь совершенно не при чем - просто в массив можно засунуть то, что в вектор не очень-то положишь - некопируемые smart-указатели, например. Или нужен статический (в смысле контекста) контейнер заданного размера - тогда тоже лучше массив - можно инициализировать списком.

Но и в этом случае я бы использовала алгоритмы STL для всяческих хождений по этому массиву - find, sort, for_each, etc, etc - ибо см. п 1)
Добавлено @ 16:33
Цитата(azesmcar @ 30.9.2005, 16:27)
практика все же показывает что я прав...

... просто ее мало... smile

Автор: azesmcar 30.9.2005, 16:43
Цитата
... просто ее мало... 


Ну что тут поделаешь...как говориться 7 лет это не время, но срок...

Цитата
1) Использование STL ускоряет написание любых программ в разы.


Я тебе привел пример когда программу твой СТЛ не ускоряет...

Код

typedef enum
{
   MyParam_NONE,
   MyParam_INT,
   MyParam_CHAR,
   MyParam_STRING,
   MyParam_Length
} MyParam;

int a[ MyParam_Length ];
a[MyParam_INT] = 5;
a[MyParam_STRING] = 8;



Выписка из кода, дополнено и исправлено, массив хранит в себе какие то индексы например...

Засунь сюда STL и покажи насколько он будет быстрее...и насколько это вообще нужно.
Добавлено @ 16:45
точнее говоря обясни мне смысл стл -а в данном примере...зачем впихивать в проект вектор?

Автор: Mad 30.9.2005, 17:20
Цитата(Earnest @ 30.9.2005, 14:41)
Ну, в общем, не совсем это правда - если говорить о хорошей реализации, STL, например.
Если не надо расширять vector, то нет там никаких операций перераспределения памяти

если ты не расширяеш вектор, то это уже статический массив smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)