Сохраните данные в файле .exe - PullRequest
0 голосов
/ 26 августа 2018

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

Таким образом, когда я снова открою его, все эти данные будут сохранены там, поэтому мне просто нужно обновить или изменить их.

Ответы [ 3 ]

0 голосов
/ 26 августа 2018

Для того, что вы хотите сделать, требуется способность записывать в (и, возможно, читать из) исполняемый файл , пока он работает . Насколько я знаю, это невозможно. Хотя можно изменить поведение запущенного исполняемого файла на основе пользовательского ввода, который он предварительно обуславливает (например, видеоигру), невозможно сохранить эти входные данные непосредственно в exe.

Видеоигры сохраняют прогресс, очки игрока (которые являются результатом входных данных от игрока) в файл (ы) вне запущенного .exe .

Таким образом, вам придется хранить данные в файле вне файла .exe. Я обычно использую буферы протокола Google, чтобы сделать это. Хорошее объяснение их можно найти здесь .

Они бесплатны, просты в использовании и поддерживаются для C ++.

Они лучше, чем другие форматы, такие как XML. Некоторые из преимуществ упомянуты здесь

Буферы протокола имеют много преимуществ по сравнению с XML для сериализации структурированных данных.

Протокол буфера :.

  1. проще
  2. в 3-10 раз меньше
  3. в 20-100 раз быстрее
  4. менее неоднозначны
  5. генерирует классы доступа к данным, которые проще использовать программно
0 голосов
/ 28 августа 2018

управление датами рождения

Как я объясняю в моем другом ответе (который вы должны прочитать перед этим), вы не хотите сохранять данные внутри ваш .exe файл.Но я предполагаю, что вы хотите сохранить дату рождения пользователя (и другие данные) от одного прогона к другому.Этот ответ сфокусирован в основном на аспекте «дата рождения пользователя» и предполагает, что ваш вопрос представляет собой некоторую проблему XY (вас действительно волнуют даты рождения, а не перезапись исполняемого файла).

Итак,вы решаете оставить их где-нибудь (но вне вашего исполняемого файла).Это может быть текстовый файл;возможно, используя JSON или YAML формат, или какой-либо другой текстовый формат файла , который вы определите правильно, указанный в нотации EBNF в некотором документе;или двоичный файл (возможно, буфер протокола как , предложенный PW , или какой-то sqlite «файл базы данных», или ваш собственный двоичный формат, который вам необходимо правильно документировать).Очень важно правильно задокументировать формат файла, который вы используете.

Работать с текстовым файлом (формат которого вы четко определили) легко, просто открыв fopen.Сначала вам нужно определить четко определенный путь к файлу, возможно, такой простой, как

#define MYAPP_DATA_PATH "mydata.txt"

или лучше

const char* myapp_data_path = "mydata.txt";

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

Возможно, вам также понадобитсяорганизовать некоторую структуру данных (возможно, глобальную переменную MyData global_data;), хранящую эти данные.В C ++ вы определите некоторый class MyData; и захотите, чтобы он имел как минимум функции-члены, такие как void MyData::add_birth_date(const std::string& person, const std::chrono::time_point& birthdate); и void MyData::remove_birth_date(const std::string& person);.Возможно, у вас будет больше классов, таких как class Person; и т. Д. *

с использованием текстового формата

Так что ваше приложение начинается сначала с заполнения global_data , если файл mydata.txt существует (в противном случае ваш global_data сохраняет свое пустое начальное состояние).Это просто, у вас будет какая-то функция инициализации, например:

void initial_fill_global_data(void) {
  std::ifstream input(myapp_data_path);
  // the file opening could have failed.... then we return immediately
  if (!input || !input.good() || input.fail()) 
     return;

Конечно, вам нужно разобрать это input.Используйте хорошо известные методы синтаксического анализа , которые будут вызывать global_data.add_birth_date соответственно.Обратите внимание, что для формата JSON вы найдете хорошие библиотеки C ++ (такие как jsoncpp ), чтобы сделать это действительно просто.

Перед выходом из приложения вы должны сохранить этот файл.Поэтому вы бы вызвали функцию save_global_data, которая выводит в файл mydata.txt содержимое MyData.Кстати, вы можете даже зарегистрировать его с std::atexit.

Функции initial_fill_global_data и save_global_data могут быть функциями-членами (возможно, static) вашего класса MyData.

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


с использованием sqlite файла базы данных

Я также предложилхранить ваши данные в файле базы данных sqlite .Прочитайте учебник по sqlite и обратитесь к справочной документации по sqlite C & C ++ .Тогда вам нужно подумать о хорошо спроектированной схеме базы данных .И вам больше не нужно хранить все данные в памяти, поскольку sqlite способен обрабатывать большой объем данных (много гигабайт), больше, чем умещается в памяти.

Очевидно, вам нужен глобальный указатель базы данных.Итак, объявите некоторые глобальные sqlite3*db;.Конечно, myapp_data_path теперь это какой-то "mydata.sqlite" путь.Ваш main начинается с открытия (и создания пустой базы данных, если необходимо) с помощью

int opsta = sqlite3_open(myapp_data_path, &db);
if (opsta != SQLITE_OK) {
   std::cerr << "failed to open database " << myapp_data_path
             << " with error#" << opsta << "=" << sqlite_errstr(opsta) 
             << std::endl;
   exit (EXIT_FAILURE);
} 

Если база данных не существует, она создается пустой.В этом случае вам необходимо определить соответствующие таблицы и индексы в нем.Моим первым предложением может быть что-то простое, как

char* errormsg = NULL;
int errcod = sqlite3_exec(db, 
    "CREATE TABLE IF NOT EXISTS data_table ("
    " name STRING NOT NULL UNIQUE,"
    " birthdate INT"
    ")", 
    &errormsg);
if (errcod != SQLITE_OK) { 
    std::cerr << "failed to create data_table " << errormsg << std::endl;
    exit(EXIT_FAILURE);
}

Конечно, вам нужно подумать о более умной схеме базы данных (на самом деле вам нужно несколько таблиц, немного нормализации базы данных , и вы должны умело добавить индексирует в ваших таблицах) и готовит запросы (преобразуют их в sqlite_stmt -s), выполненные в вашей программе.


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

0 голосов
/ 26 августа 2018

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

Есть ли способ создать [...] программу, которая [...]запрашивает входные данные [...], а затем сохраняет эти данные в этом .exe, когда я его закрываю?

Простого пути нет (но он вам не нужен).То, что вы хотите, связано с постоянством и проверкой приложения .

На практике вы, вероятно, хотите хранить данные в файле, а не в исполняемом файле (возможно, используя некоторыетекстовый формат, такой как JSON ) или в некоторой базе данных (возможно, такой простой, как некоторые sqlite , или взаимодействие с некоторыми RDBMS , такими как PostGreSQL ).Для таких вещей, как дни рождения и предпочтения в еде, файл базы данных sqlite, вероятно, является хорошим подходом (см. Руководство по SQLite ).Приложите усилия к хорошему дизайну вашей схемы базы данных .

Таким образом, когда я открою ее снова, все эти данные все равно будут сохранены там

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

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

Обычная практика - хранить данные вне исполняемого файла (большинство программ, включая ваш текстовый процессор, ваш компилятор, ваш веб-браузер, ... делают это).Вы не объясняете, почему вы хотите хранить некоторые данные в исполняемом файле, и это необычно и очень специфично для конкретной операционной системы и формата исполняемого файла (для Linux изучите внимательно elf (5) ...)

Я бы предложил сохранить данные в каком-то необязательном файле (или базе данных) - его путь к файлу может иметь некоторую постоянную по умолчанию для проводной связи и т. Д ....При запуске вы проверяете наличие этих данных (например, с помощью access (2) в POSIX или просто обрабатывая случай сбоя fopen или sqlite3_open и т. Д ....).Если он не существует, вы каким-то образом инициализируете данные вашей программы.При выходе (или экономии времени) вы записываете эти данные.Кстати, большинство программ делают это.

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

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

Для полноты картины некоторые программы действительно записывают некоторые данные, переписывая свой исполняемый файл.В Linux GNU emacs делает это (на практике, только во время процедуры установки) в своем файле unexec.c (очень хрупкий, так как зависит от ОС и компилятора), но это оспаривается и, вероятно, исчезнет.

Многие другие системы разумно справляются с ортогональным постоянством: SBCL имеет некоторый примитив save-lisp-and-die (обычно он сохраняется в каком-либо другом файле "изображения"). Poly / ML имеет некоторое export средство .Система J.IAPratrat CAIA (см. этот документ и его блог ; архив с CAIA 2016 доступен - с разрешения - на my домашняя страница) может полностью восстановить весь код C и все необходимые данные (в тысячах файлов). FullPliant сохраняет свое состояние в хорошо организованном дереве файлов.Такие методы сохранения или контрольных точек связаны с сборкой мусора (поэтому вам следует прочитать руководство GC ) и используют методы и алгоритмы, близкие к копированию сбора мусора.

FWIW, мой текущий проект, bismon , ортогонально сохраняет всю свою кучу, но делает это вне основного исполняемого файла (в идеальном мире я хотел бы заново сгенерировать весь исходный код C или C ++это; я далек от этой цели).

Я рекомендую хранить ваше программное обеспечение в нескольких файлах: его исполняемый файл, связанный с ним исходный код C ++, его файлы данных (и, вероятно, гораздо больше зависимостей, то есть общихбиблиотеки или библиотеки DLL, файлы шрифтов и изображений, необходимые двоичные файлы и т. д.).Тогда вам не нужно перезаписывать свой исполняемый файл при сохранении вашего состояния.Поскольку вы упоминаете C ++ (который является , а не homoiconic ), вы можете сгенерировать код C ++ вашей системы (затем называемый Quine программой) с его постоянными данными (и оставьте перекомпиляцию всего, что сгенерировало C ++, системному компилятору C ++).Я также рекомендую сделать вашу самогенерирующую программу свободным программным обеспечением .(если вы сделаете это, было бы неплохо отредактировать ваш вопрос так, чтобы он указывал его URL).

В C ++ вы можете хранить данные внутри исполняемого файла (опять же, это плохая идея ,и я надеюсь убедить вас избегать такого подхода) следующим образом: вы добавляете один исходный файл на C или C ++ (например, mydata.cc), который содержит только данные (например, некоторые большие const char data[]="... many lines of data ...";) - кстати, Формат файла XBM может быть вдохновляющим.Вы сохраняете все остальные *.o объектные файлы (в месте, известном вашей программе).Чтобы сохранить данные, вы регенерируете этот mydata.cc файл (с новыми данными для вашего текущего состояния) при каждой операции сохранения, и, наконец, вы запускаете соответствующие команды (возможно, используя std :: system в вашем коде), чтобы скомпилировать mydata.cc и связать его с сохраненным *.o в новый исполняемый файл.Поэтому для каждой операции сохранения требуется перекомпиляция data.cc и ее связывание с другими объектными файлами *.o (и, конечно, компилятор и компоновщик C ++, возможно, с дополнительными инструментами автоматизации сборки, становится необходимой зависимостью вашей программы).Такой подход не проще, чем сохранение внешнего файла данных (и в любом случае требует сохранения этих *.o объектных файлов).

Таким образом, когда я открою его снова, все эти данные будутвсе еще сохраняться там

Если ваша цель - просто получить данные, если они были записаны в прошлом, просто храните данные в некоторой необязательной базе данных или файле (столько же программ, сколькоdo: ваш текстовый процессор попросит вас сохранить свой документ перед выходом, если вы запустите его без какого-либо документа и напишите в нем несколько слов) вне вашего исполняемого файла и напишите его перед выходом из программы.Не нужно перезаписывать ваш исполняемый файл!

...