Sovrascrivere su SD, si può ?

Ciao, ho cercato ma non sono riuscito a trovare un topic che ne parla, esite un modo per sovrascrivere un file nella memoria SD ? senza necessariamente cancellare-ricreare-scrivere ? Nei comandi della libreria non ho trovato niente ( almeno credo). Ho trovato una risposta nel forum in inglese, ma confesso che non ci ho capito granchè. :(

Grazie !

Purtroppo la libreria SD, quando apre un file in lettura, posiziona automaticamente il file pointer alla fine dello stesso, per cui ogni scrittura andrà ad accodarsi al file.

Puoi manualmente spostarti all'inizio del file dopo averlo aperto, con file.seek(0), tuttavia il file non viene troncato, per cui se scrivi meno byte di quanti ce n'erano prima, i restanti rimarranno lì.

La cosa più sicura è cancellare e riscrivere. Oppure scrivere in un file nuovo e rinominarlo sul vecchio, ma non sono sicuro che funzioni.

EDIT: È possibile ovviare a questo non aprendo il file con FILE_WRITE ma con flag specifici, vedi messaggi sottostanti.

questo accoda le linee

file.open(&root, "datalog.txt", O_APPEND | O_WRITE);
  file.println(log_riga);
  delay(1);
  file.close();

questo svuota il file che non lo elimina e a tutti gli effetti diventa 0Kb

file.open(&root, "datalog.txt", O_TRUNC | O_WRITE); 
  delay(1);
  file.close();

Il problema vero è che nella libreria SD si sono dimenticati di implementare un metodo … truncate() che avrebbe risolto i problemi di Vik1 … ::slight_smile:

Però, se veramente serve, facendo un po’ di ricerche con Google si trovano librerie compatibili Arduino, per la gestione delle SD, che tale metodo lo implementano … se non vado errato proprio la SdFat … ::slight_smile:

Guglielmo

si che c'è ... l'ho scritto nel post #2 e sono certo che funziona

O_READ - Open for reading.

O_RDONLY - Same as O_READ.

O_WRITE - Open for writing.

O_WRONLY - Same as O_WRITE.

O_RDWR - Open for reading and writing.

O_APPEND - If set, the file offset shall be set to the end of the file prior to each write.

O_CREAT - If the file exists, this flag has no effect except as noted under O_EXCL below. Otherwise, the file shall be created

O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists.

O_SYNC - Call sync() after each write. This flag should not be used with write(uint8_t), write_P(PGM_P), writeln_P(PGM_P), or the Arduino Print class. These functions do character at a time writes so sync() will be called after each byte.

O_TRUNC - If the file exists and is a regular file, and the file is successfully opened and is not read only, its length shall be truncated to 0.

Hai ragione! Non sapevo che avessero definito anche i classici flag di apertura Unix, credevo si fossero limitati a definire i loro FILE_READ e FILE_WRITE. Ottimo, ritiro dunque quanto detto prima!

EDIT: gpb intende la classica funzione C ftruncate(). Quella effettivamente non c’è, ma quel flag di apertura (che c’è), fa la stessa cosa in questo caso.

ecco un esempio

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>
#define DEBUG_SD 1

byte err_sd=1;
//byte err_eth=1;
//byte err_ntp=1;

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };  //default
byte ip[] = {192,168,2,177};

Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;

void setup() {
  delay(1000);
  Serial.begin(9600);//debug
  Serial.println("Setup in corso ...");//debug  
  pinMode(4, OUTPUT);   digitalWrite(4, 1);  
  pinMode(10, OUTPUT);  digitalWrite(10, 1); 

  sd_init();
  delay(50);
  Ethernet.begin(mac, ip);  

  Serial.println("Svuoto file datalog.txt");//debug  
  file.open(&root, "datalog.txt", O_TRUNC | O_WRITE); 
  delay(1);
  file.close(); 
  
}

void sd_init() {   
  err_sd=0;
  if (!card.init(SPI_FULL_SPEED, 4)) err_sd=1;
  if (!volume.init(&card)) err_sd=1;
  if (!root.openRoot(&volume)) err_sd=1;
  
  #if DEBUG_SD 
  if (err_sd==0) 
  Serial.println("inizializzazione SD completata.");//debug  
  else 
  Serial.println("inizializzazione SD fallita.");//debug
  #endif
 
  if (err_sd==0){ 
  log_write("---------------- Inizializzazione sistema in corso ----------------");
  log_write("inizializzazione SD completata!"); 
  }
}  


void  log_write(String log_riga){
  //scrittura su file txt sd .... eventi
  
  file.open(&root, "datalog.txt", O_APPEND | O_WRITE);
  file.println(log_riga);
  delay(1);
  file.close(); 
 
}


void loop() {

}

Ottimo, grazie x le risposte. farò un pò di prove.

No, un attimo, io per truncate() intendevo che [u]mi posiziono[/u] in un certo punto (seek()) e tronco IN QUEL punto (accorcio il file), non dall'inizio (lunghezza zero) ... questo come si fa ?

In pratica ... come aggiorno il EOF pointer al punto che voglio io ?

Guglielmo

Quello non pare si possa fare. Si può troncare solo contestualmente all'apertura, ergo a dimensione 0.

SukkoPera: Quello non pare si possa fare. Si può troncare solo contestualmente all'apertura, ergo a dimensione 0.

Ma questo non serve a una mazza .. emm ... serve a poco ... :grin:

A me interessa la gestione [u]vera[/u] del file ... quindi il poterlo troncare alla lunghezza che dico io ;)

Guglielmo

Eccola ... :grin:

/
/------------------------------------------------------------------------------
/** Truncate a file to a specified length.  The current file position
 * will be maintained if it is less than or equal to \a length otherwise
 * it will be set to end of file.
 *
 * \param[in] path A path with a valid 8.3 DOS name for the file.
 * \param[in] length The desired length for the file.
 *
 * \return The value one, true, is returned for success and
 * the value zero, false, is returned for failure.
 * Reasons for failure include file is read only, file is a directory,
 * \a length is greater than the current file size or an I/O error occurs.
 */
bool SdFat::truncate(const char* path, uint32_t length) {
  SdFile file;
  if (!file.open(path, O_WRITE)) return false;
  return file.truncate(length);
}

Guglielmo

Edit: Ecco la libreria a cui mi riferivo ... QUI ;) ... Ovvio che occorre verificare se tutto funziona e se è esente da buchi ... ma almeno la traccia c'è ;)

Si, io ho capito che volesse sovrascrivere il file senza un delete e create... E avevo un pezzo pronto che ho utilizzato con successo. In effetti sostituire solo una linea del file o una parte di esso resta più complicato ma non impossibile.... Se però l'unico modo è fare una copia col nuovo testo, eliminare l'originale e rinominare il nuovo file, per una mcu così lenta e limitata non mi ci metto neanche

Sì sì, con librerie alternative si può fare tranquillamente. Io ad esempio uso questa, che supporta anche i filename lunghi, e prevede anche il truncate:

https://github.com/greiman/SdFat

Mi sembra mooooolto simile a quella che hai linkato tu ;).

Prima volevo solo dire che con la libreria SD inclusa di default in Arduino non sembra sia possibile.

@pablos: Non è detto che riscrivere il file da capo ogni volta sia una cosa particolarmente pesante. Dipende da quanto è grosso, ad esempio. In un ambiente ristretto come Arduino direi che bisognerebbe comunque cercare di evitarlo e limitarsi all'append, se possibile.

SukkoPera: https://github.com/greiman/SdFat Mi sembra mooooolto simile a quella che hai linkato tu ;).

... ho idea [u]sia la stessa[/u], ma la tua è più aggiornata! :grin: :grin: :grin:

Guglielmo