Как сохранить структуру в файле?
Я пытаюсь сохранить (прочитать и записать() структуру, используя LittleFS
на ESP8266.
Благодарим за помощь
struct oper_string
{
bool state; /* On or Off */
uint8_t step; /* Step, in case of PWM */
uint8_t reason; /* What triggered the button */
time_t ontime; /* Start Clk */
time_t offtime; /* Off Clk */
};
@Guy . D, 👍0
3 ответа
Лучший ответ:
вы можете думать о структуре как о массиве (группе) переменных, которые могут иметь разные размеры, и структура помогает вам взять определенную часть этой строки. Допустим, у вас есть структура
struct foo{
bool first;
uint8_t Second;
uint16_t Third;
} structMember;
Затем он сохраняется в строке в том же порядке, в котором они были определены
---------------------------
| bool | uint8_t | uint16_t|
|1 byte| 1 bytes | 2 bytes |
--------------------------
1B+1B+2B = sizeof(srtuctMember)
Файл.write() сохраняет дату в файле блоками по 1 байт каждый.
Когда вы отправляете массив в функцию, она фактически получает только указатель на свое начало. Вот почему многие функции принимают размер в качестве второго аргумента.
Когда вы отправляете данные в функцию в формате, для которого она не предназначена, вы можете указать компилятору, что функция должна определить их как переменную другого типа.
Допустим, у нас есть функция, определенная как
void sampleFunction (byte var1)
то мы можем использовать его
char x='j';
sampleFunction((byte) x);
который указывает компилятору обрабатывать 'x' как если бы он был byte
.
когда вы передаете массив, вы на самом деле передаете не фактические данные, а информацию о том, где находятся данные (то же самое с членом структуры), поэтому, ставя звездочку после определения, вы сообщаете компилятору формат данных указывает на. и когда вы прочитаете его обратно, вы получите все спорные данные как те же данные в том же порядке... какая структура затем делится таким же образом
file.write((byte*) &StructMember,sizeof(structMember));
Как дополнение к ответу Томаса (что совершенно справедливо), я хотел бы указать, что последовательные члены структуры не могут быть рядом в памяти. Некоторые структуры имеют отступы внутри них, т.е. необходимо для удовлетворения требований к выравниванию.
Структура, приведенная Томасом в качестве примера, не будет иметь заполнения, но ваша май. Например, на моем 64-битном ПК ваша структура хранится следующим образом:
смещение | размер | участник |
---|---|---|
0 | 1 | состояние |
1 | 1 | шаг |
2 | 1 | причина |
3 | 5 | (заполнение) |
8 | 8 | вовремя |
16 | 8 | нерабочее время |
В приведенной выше таблице "смещение" – это положение элемента относительно
начало структуры. Заполнение после reason
необходимо для того, чтобы
чтобы выровнять ontime
по адресу, кратному 8. Ваш ESP
будучи 32-битной машиной, она, скорее всего, будет использовать только один байт
заполнение.
Заполнение может быть проблемой по двум причинам:
В зависимости от компьютерной архитектуры он может различаться. Таким образом, если вы планируете запишите файл на ESP и прочитайте его на своем ПК (или наоборот). наоборот), это один из возможных источников несовместимости, другие — порядок следования байтов и размеры элементов.
Это пустая трата места на диске, хотя это может не быть проблемой, если вы теряете только 1 байт из 20.
Если заполнение может быть проблематичным, единственным допустимым вариантом является запись и читать структуру по одному члену за раз. И наоборот, если файл только когда-либо будут доступны для ESP, и вы не против потерять несколько процент файла до заполнения, тогда не волнуйтесь, следуйте совету Томаса, и читайте и записывайте структуру как массив байтов: это просто проще.
спасибо за эту точку зрения (это была догадка, которую я получил, комментируя ответ Томаса).
У меня есть 3 других файла на этом ESP (может быть ESP32 или ESP8266), в которых хранятся параметры.
Другой вариант, о котором я думал, - это сохранить его в виде строкового файла value0;value1;value2
... с разделителем в качестве маркера.
И, наконец, используя ArduinoJSON в качестве пары ключ/значение, снова сохраните его аналогично файлу параметров, который у меня есть на борту.
Чтобы вы посоветовали ?, @Guy . D
Спасибо, я собирался сослаться на отступы как на часть или причину использования sizeof, а не на конкретный номер размера, и хотел указать на это в отношении логического значения, но каким-то образом он потерялся при редактировании, пытаясь не писать слишком много точек. Я признаю, что я IDYer, и пытаюсь написать свои ответы на более простом уровне. И спасибо за улучшение всех моих ответов здесь :-) у вас есть права на редактирование, @Tomas
@guyd: я почти всегда предпочитаю текстовые форматы двоичным: их несколько сложнее анализировать на компьютере, но они намного удобнее для людей, которые собираются отлаживать материал. Двоичный — это выбор, когда вам нужна эффективность использования пространства (либо в самом файле, либо в коде, который его обрабатывает) или производительность ввода-вывода. При этом: все варианты, которые вы предлагаете, действительны. Выберите формат, который вам наиболее удобен., @Edgar Bonet
@Томас: Спасибо! Иногда я придираюсь. Ваши ответы являются полезным и ценным вкладом, и улучшение того, что уже хорошо, кажется весьма полезным. ;-), @Edgar Bonet
Спасибо вам обоим за полный ответ! Парень, @Guy . D
структуру можно записать непосредственно в файл SPIFS/FFAT следующим образом:
* EXAMPLE READING AND WRITING A C++ STRUCT TO A FILE
*/
#include <FS.h>
#include <SPIFFS.h>
typedef struct {
char someString[10];
float someFloat;
} MyStruct;
MyStruct myStruct = { "test test",1.7 };
void setup() {
// поместите сюда код установки для однократного запуска:
Serial.begin(115200);
if(!SPIFFS.begin()){
Serial.println("Card Mount Failed");
return;
}
strncpy( myStruct.someString, "testtesttest", sizeof(myStruct.someString) );
File myFile = SPIFFS.open("/somefile.txt", FILE_WRITE);
myFile.write((byte *)&myStruct, sizeof(myStruct));
myFile.close();
}
void loop() {
// поместите сюда ваш основной код для многократного запуска:
File myFile = SPIFFS.open("/somefile.txt", FILE_WRITE);
myFile.read((byte *)&myStruct, sizeof(myStruct));
myFile.close();
Serial.printf( "read: %s, %.2f\n", myStruct.someString, myStruct.someFloat );
delay(1000);
}
приведенный выше код доступен в этом Github Gist.
Придирка: если карта не монтируется, setup()
должна abort()
, а не return
., @Edgar Bonet
Я согласен. Не хватает нескольких проверок кода передовой практики, чтобы это работало безупречно. Still — это звуковой фрагмент кода для использования., @Miguel Silva
@MiguelTomás Предполагаете, что это будет работать так же, используя LittleFS
?, @Guy . D
честно говоря, не пробовал в последнее время. однако я не вижу причин, почему это не сработает..., @Miguel Silva
@MiguelTomás - я пытаюсь понять, почему myFile.read((byte *)&myStruct, sizeof(myStruct));
используется (byte *)
? Я бы никогда не догадался о таком синтаксисе, @Guy . D
правильно... я могу порекомендовать несколько хороших видеоуроков на YouTube, в качестве альтернативы - coursera.org или udemy, @Miguel Silva
- Любой способ использовать DMA для передачи данных на SD - карту в ESP8266
- Где хранить критические данные в ситуации отключения электроэнергии на ESP8266
- ОТА-программа SPIFFS на ESP8266
- Как записать и прочитать из файла SPIFFS как данные объекта на ESP8266
- ESP32 - ошибка при использовании LITTLEFS.h после обновления ядра до 2.0.4
- Сбой при обслуживании файлов статической ширины ESP8266WebServer
- SPIFFS и LittleFS не показывают все мои файлы с readdir() и не могут открыть() их
- Ошибка сохранения SPIFFS на ESP8266 - только после 3-й записи
Я только хочу, чтобы кто-нибудь объяснил мне это, когда я изо всех сил пытался понять, как это связано с char и utf8 несколько лет назад..., @Tomas
Итак, идея в том, что каждый элемент в этой структуре упоминается как указатель
byte
/utin8_t
? и результатом является массив указателей (без каких-либо разделителей, пробелов и т. д.)?, @Guy . D@guyd:
StructMember
— это экземпляр, а не член структуры. Идея состоит в том, что структура в целом должна обрабатываться как массив байтов. Точнее, _address_ структуры следует рассматривать как начальный адрес массива байтов. Это указатель на массив (байтов), а не на массив указателей., @Edgar Bonet@EdgarBonet - извините за мой английский, но членом структуры является, например,
offtime
, на который ссылается адрес (&
). Спасибо, что прояснили это., @Guy . D