Bestimmte Textstellen von einer Datei auf einer SD Karte ändern

Hallo Forum,

ist es möglich bestimmte Textstellen von einer Datei auf einer SD Karte zu ändern, ohne die alte Datei kopieren, die Stelle löschen/ändern und dann neu speichern zu müssen ?

Habe bisher keinen anderen Weg gefunden, das ganze dauert jedoch relativ lang (ca. 1 Minute bei einer Datei mit 2300 Zeichen). Da wäre es doch besser nicht die Datei zu kopieren und zu ändern, sondern nur die Stelle direkt zu ändern/löschen.

Hab ihr da irgendwelche Ideen?

Gruß Moritz

Kommt auf die Daten an. Für CSV Dateien speichere alle Datensätze so ab, dass die Zahlen eine konstante Breite haben. Dann kannst du die Zeichen oder Anzahl der Zeilen zählen und gezielt überschreiben.

Wenn wir jetzt aber davon ausgehen, dass ich eine CSV Datei benutze, die Datensätze variabel groß sein können, diese durch semikolons getrennt sind und ich möchte anstatt (Beispiel) "Welt" das Wort "Computer" an der selben stelle haben, dann kann ich ja keine weiteren Zeichen einfügen ?

ich habe ein ähnliches problem, deswegen bin ich mal so frei, mich hier anzuschließen......

ich würde gerne einstellungen in eine csv-datei schreiben und diese einstellungen ddann auslesen bzw im programm verwenden.

ich habe mir ein paar tutorials dazu durchgelesen, aber ganz schlau bin ich daraus nicht geworden....

ist es möglich zu sagen, wert aus zeile3 und spalte1 auslesen ? und diesen dann einer variablen zu zuweisen.

wenn ich soweit bin und das geschafft habe, würde ich auch gerne die einstellungen ändern können......erstmal über den seriellen monitor, später über ein touch-tft und über das netzwerk....
parallel sollen allerdings auch messwerte protokolliert werden......

vielen dank !

Habe gerade mal eine weiter Idee. Man könnte doch jeden Datensatz einzeln als Datei abspeichern, den Dateinamen mit einem sinvollen Namen versehen, dann nach diesem Suchen und dann diese kleinere Datei kopieren, ändern und wieder speichern. So müsste das ganze schneller gehen.

ich fände die lösung über eine datei etwas schicker......

sonst hab ich hinterher 20-30 dateien auf der sd-karte....

und wenn ich mich nicht täusche kann man auch immer nur eine datei gleichzeitig öffnen, deswegen spiel ich mit dem gedanken, die gesamten konfigurationsdaten in den eeprom zu laden.....wie auch immer das alles nun funktionieren mag....

Einfache Zahlen kann man problemlos in einer Datei speichern und ändern. Das Auslesen habe ich hier mit der SdFat Library gezeigt:
http://forum.arduino.cc/index.php?topic=311802.0
Überschreiben geht ähnlich. Zeilen Zählen. Immer mit curPosition() den Anfang der Zeile Speichern, Zeile auslesen und wenn man die gewünschte Zeile hat den Cursor mit seekSet() wieder auf den Anfang setzen. Das wird auch in einem Beispiel gezeigt.

Geht natürlich auch mit der normalen SD Library, aber man muss da die Zeile per Hand auslesen

Man muss die Zeilen nur auf konstante Breite formatieren, z.B. mit snprintf(). Also sowas:

010,005,459
002,100,099

Wenn man wie hier Text hat wird es natürlich problematischer. Man könnte aber auch hier Wörter konstant breit machen. Dann hat man halt eine maximale Länge, z.B.:

Test____
Blahblah
Taster__

statt '_' ein Leerzeichen

sorry aber so ganz verstanden hab ich nicht, was da passiert.....

was muss ich denn tun, wenn die csv zb so aussieht:

spalte1 ; spalte2

überschrift
wert1 ; 55
wert2 ; 23
überschrift
z1 ; 08:00:00
z2 ; 16:00:00
i1 ; 30
usw ; usw

und im program würd ich die werte gerne verwenden:

int w1 = 55
usw

und für den seriellen monitor:

seriell.print ("wert 1 = ");
seriell.print (w1);
usw

Am einfachsten ist es man lässt die Variablen-Bezeichnungen ganz weg, oder man packt sie in die erste Zeile und die Werte in die zweite Zeile:

Wert1,Wert2, Zeit1,Zeit2:
001,150,08:00:00,16:00:00

Oder so:

Wert1,Wert2:
001,150
Zeit1,Zeit2:
08:00:00,16:00:00

Aber nicht durcheinander Überschriften in einer Zeile und dann noch Bezeichner in der gleichen Zeile wie die Werte. Das kann man zwar auch auslesen und auswerten, aber es ist komplizierter als es sein müsste. Gerade in deinem Fall, wenn du solche Fragen stellst und wahrscheinlich nicht sicher mit Strings umgehen kannst.

Der Punkt ist, dass man die Bezeichner im Code wahrscheinlich eher ignoriert und einfach nach der Reihenfolge geht. Die sind eher dafür da, dass man in der Text Datei weiß was was ist. Natürlich könnte man in der Text-Datei nach "Wert1" suchen, aber der Aufwand lohnt sich nicht wirklich.

ok ! vielen dank !
sehr gute idee !

dann mach ichs nach dem schema:

überschrift1
min. max. start stopp
wert1; wert2; zeit1; zeit2;

überschrift2
min. max. start stopp
wert1; wert2; zeit1; zeit2;

überschrift3
start stopp
zeit1; zeit2;

überschrift4
min. max.
wert1; wert2;

usw (manche zeilen enthalten mehrere variablen, manche nur eine oder zwei - manche nur "zahlen", manche uhrzeiten)

und lese dann nur die 3te zeile und die 7te zeile aus... und weise diese dann variablen zu.....

und wie mach ich das ? mit einem array oder string ? (kenn mich mit beidem nicht aus)

geht das auch mit der normalen sd-libary ? weil mit der hab ich jetzt einmal angefangen..

hier macht jemand das mit der parseint-funktion.........

ich habe nur keine ahnung, was das bedeutet bzw, wie diese funktion jetzt an die werte in der konfigurationsdatei kommt......

ich hab mir jetzt noch einige seiten dazu durchgelsen, aber leider nur die hälfte verstanden.........

ich verstehe immer noch nicht, wie ich dem arduino sagen kann, das er zb zeile 4 auslesen soll und die anderen überspringen soll.....

und wie ich dann zb den 4ten wert als variable definiere......

Du musst das halt zeilenweise einlesen. Was dir fehlt sind Programmierkenntnisse, sowohl was die Sprache an sich betrifft, als auch das Ausdenken von Algorithmen. Gerade bei etwas anspruchsvolleren Themen wie hier, musst du da Grundlagen lernen, d.h. u.a. C Strings und ein paar der Standard C String Funktionen. Etwas Zeigerarithmetik schadet in deinem Fall auch nichts.

Wie es mit SdFat geht habe ich gezeigt. Mit der SD Library musst du mehr per Hand machen. Dabei kann man sich an Code für das Einlesen von der seriellen Schnittstelle halten:
http://forum.arduino.cc/index.php?topic=276430.msg1945602#msg1945602

So ähnlich wie da eine Zeile von Serial bis zum einem CR oder LF eingelesen wird, kann man das auch mit der SD Library machen (Zeilen sind mit CR/LF abgeschlossen). Also wenn du zwei CR oder LF eingelesen hast bist du am Ende der 2. Zeile.
Plus noch etwas Logik um zu Erkennen dass man am Ende der Datei ist.

Wenn man dann die gewünschte Zeile in einem char Array hat, nimmt man strtok() zum Splitten und atoi() zum Wandeln in einen Integer (oder atof() for Floats). z.B. um eine Uhrzeit wie "16:25:30" zu verarbeiten die in einem Array namens "buffer" steht:

byte hour = atoi(buffer);
byte minute = atoi(buffer + 3);
byte second  = atoi(buffer + 6);

Ob das jetzt von Serial oder SD kommt macht keinen Unterschied. Auslesen und Parsen sind zwei getrennte von einander unabhängige Schritte (weshalb man das auch in unterschiedlichen Funktionen macht)

vielen dank !

ich hab jetzt mal "klein" angefangen.... und machs schritt für schritt.....

die zeilen auslesen kann ich damit......das "aufsplitten" mach ich dann wenn das klappt.....

allerdings kann ich den wert der zeile leider nicht der variablen zuweisen

"
#include <SD.h>
#include <SPI.h>

File myFile;

char x;

void setup()
{

Serial.begin(9600);

pinMode(53, OUTPUT);

if (!SD.begin(4)) {
return;
}

char MyChar;
myFile = SD.open("test.csv");
if (myFile)
{

int Zeile = 1;

while (myFile.available())
{
MyChar = myFile.read();

if (MyChar=='\n')
{

Serial.println();
}
else
{
switch (Zeile)
{
case 1:
Serial.print(MyChar);
break;
case 2:
Serial.print(MyChar);
break;
case 3:
Serial.print(MyChar);
x = MyChar;
break;
case 4:
Serial.print(MyChar);
break;
case 5:
Serial.print(MyChar);
break;
case 6:
Serial.print(MyChar);
break;
case 7:
Serial.print(MyChar);
break;
case 8:
Serial.print(MyChar);
break;
case 9:
Serial.print(MyChar);
break;

default:
break;
}

}

}

myFile.close();

Serial.print("x=");
Serial.print(x);
}
else
{
Serial.println("error opening test.txt");
}

}

void loop()
{

}
"

CSV Dateien sind dafür gedacht, daß sie am Stück in den Speicher geladen werden. Zeilenweises Lesen ist zwar möglich, wenn genügend Speicher zum Puffern zur Verfügung steht, aber das Zurückschreiben geänderter Zeilen mit anderer Länge erfordert eine komplette Kopie des Rests der Datei. Dafür reichen die paar Bytes RAM nicht immer aus.

Damit empfiehlt sich eine feste Zeilenlänge, egal ob mit Daten oder Leerzeichen aufgefüllt, dann kann man solche Dateien auch zeilenweise ändern. Auf der SD Karte sollte der Platz dafür ausreichen. Noch eleganter wären binäre Datensätze gleicher Länge, nur sind die dann eben nicht mehr so einfach (als Text) zu ändern.

Damit stellt sich für mich die Frage, wer die Dateien ändert - der Benutzer oder ein Programm? Und bestehen die Daten aus Listen gleicher Struktur, oder sind es einfach nur eine Menge unterschiedlicher Werte?

vielen dank !

also das protokollieren von messwerten auf sd war ja ganz eifnach, aber das einlesen von variablen ist für mich echt verdammt kompliziert (;

in meinem fall ist es so, das die daten in der csv erstmal durch mich geändert werden sollen..später über wlan und noch später vielleicht über ein touchpanel......

da ich ja mittlerweile zeilen einlesen kann........

wäre es dann nicht doch leichter, die tabelle so aufzubauen?:

"spalte1 ; spalte2

überschrift
wert1: ; 55
wert2: ; 23
überschrift
z1: ; 08:00:00
z2: ; 16:00:00
i1: ; 30
usw ; usw"

mit parseint würd ich ja dann nacheinander die werte auslesen können und mit dem sketch oben, kann ich so zumindest die zeile auslesen und hab dann immer nur eine variable pro zeile......
kann man dann nicht die erste zeile mit der beschriftung "ignorieren" (indem man sagt, erst ab ":"(oder wahlweise "=") lesen?) und die zweite dann einer variablen zuweisen ?

ich hab dann auch immer nur ein format pro zeile......
entweder 55 oder eine 08:00:00 usw.....
uhrzeit könnte man natürlich auch 080000 oder 08;00;00 schreiben.....

ist dann nicht auch das überschreiben einfacher ? wenn man quasi sagt "zeile 4 suchen und ab ":" überschreiben". (mit dem überschreiben hab ich mich noch gar nicht auseinander gesetzt)

in meinem fall ist es so, das die daten in der csv erstmal durch mich geändert werden sollen..später über wlan und noch später vielleicht über ein touchpanel......

-> Muss also nicht unbedingt .csv sein und du hast die Freiheit, eindeutige Kennzeichen zu verlangen:
z.B.

  • Zeilen ohne ':' werden ignoriert
  • Bei Zeilen mit : steht der Wert in der 2. Spalte und welche Variable (und damit das Format) ergibt sich aus der 1. Spalte (bis zum ':')

mit dem überschreiben hab ich mich noch gar nicht auseinander gesetzt

Die Probleme sind wohl einen Level tiefer als du vermutest:

Wenn du die Datei auf dem PC änderst ( mit Excel oder einem Editor ) ist die komplette Datei im RAM und wird komplett neu geschrieben, nicht nur einzelne Zeilen.
Das ist ausser bei ganz kleinen Dateien, auf dem Arduino nicht möglich.

Wenn du da eine Zeile überschreibst, ist das danach das Ende der Datei, ausser du machst was dagegen.
Wenn es nicht das Ende der Datei ist, musst du buchstabengenau bis zum nächsten Zeilenwechsel den vorhandenen Inhalt ersetzen.

Viel Spass dann :wink:

magictrips:
da ich ja mittlerweile zeilen einlesen kann........

Nein, kannst du nicht. Du gibt eine Zeile einfach Zeichen für Zeichen auf Serial aus. Das ist nicht was du willst. Du hast noch nicht verstanden was du machen musst.

Wegen .csv:
Richtige endlose CSV Dateien sind das natürlich nicht. Aber es ist trotzdem von Vorteil Werte in einer Zeile mit einem Komma oder Strichpunkt zu trennen. Dann kann man eine Zeile ganz einlesen und strtok() drüber laufen lassen

"Du hast noch nicht verstanden was du machen musst."
-stimmt leider !! das is mir irgendwie auch noch alles eine nummer zu hoch......
ich lerne aber wesentlich besser mit "learning by doing".......
gibt es denn irgendwo ein einfach verständlichen beispielsketch oder ein tutorial, wo ganz einfach beschrieben wird, wie man einen bestimmten wert, aus einer bestimmten zeile ausließt ?

kann ich denn was mit dem sketch am ende anfangen ? oder ist mit einer "erweiterung" dieses sketches kein überschreiben möglich ?
http://forum.arduino.cc/index.php?topic=218272.0

ist denn das, was ich vorhabe überhaupt möglich ? , also hardwaremäßig mit dem arduino mega ?
ich möchte im minuten takt über 10 messwerte protokollieren (csv auf sd-karte),
30-50 einstellungen in einer config-datei(auf sd-karte) speichern und diese einstellungen auch über wlan 8eventuell touchpannel) ändern können (ohne zu reseten).....
mehrere zeitschaltuhren sollen auch noch laufen

Klar ist das möglich. Ich habe das auch genauso so am Laufen. Ich habe aber der Einfachheit halber auf alle Bezeichner in der Datei verzichtet. Dadurch verliert man Flexibilität, aber alles wird wesentlich einfacher.

Aber wie du schon erkannt hast ist das selbst einer einer einfachen Variante ein paar Nummern zu hoch für dich. Wenn du nicht mal weißt wie man mit Arrays umgeht wird das nichts. Ich habe die Grundlagen einer möglichen Version auch hier auch schon verlinkt, aber das hast du dann vollkommen ignoriert.

Gib mir mal etwas Zeit. Dann schreibe ich mal eine Luxus-Variante bei der über Serial Eingabe einzelne Werte auslesen und ändern kann.