Liebes Forum,
sitze hier schon seit tagen drann und komm nicht weiter.
Ich habe einen String : [_A,index,2.80,182,]E6B1
Ich benötige in einzellnen Variablen: ,index,2.80,182,
Das ganze läuft auf einem ESP8266 und das Programm schreibe ich in der
Arduino IDE.
Der Code:
1
// # Datenarray ([_A/temp/,Index,246,]53ED) ohne CRC-Checksumme extrahieren (53ED)
Ich habe selten so einen Sch.. mit den alten Stringfunktionen gesehen.
Arduino ist C++. Da geht auch std::string mit seinen ganzen
Hilfsfunktionen. Da braucht ein Anfänger nicht aufpassen, ob die
Array-Länge reicht und ob ein Pointer noch gültig ist.
Dann pack das ganze in Funktionen, die z.B. jeweils bis zum Komma lesen,
oder das in Unterstrings aufteilen. Für so etwas hat man ja die
Funktionen erschaffen.
Mein Vorschlag: lösch den ganzen Krams, und fang noch einmal an.
PittyJ schrieb:> Mein Vorschlag: lösch den ganzen Krams, und fang noch einmal an.
Mein Vorschlag: Er soll Gärtnern und nicht programmieren. So ein Code
zeigt deutlich dass ein komplett falsches Mindset für diese Tätigkeit
vorliegt.
PittyJ schrieb:> Mein Vorschlag: lösch den ganzen Krams, und fang noch einmal an.
Na dann mal los! :)
Hatte vergessen, dass ich noch blutiger Anfänger bin!
Ich wollte mich ersteinmal da durcharbeiten und dann mit den Funktionen
rantrauen.
PittyJ schrieb:> std::string
Davon hab ich schon was gehört....
Cyblord -. schrieb:> Mein Vorschlag: Er soll Gärtnern und nicht programmieren. So ein Code> zeigt deutlich dass ein komplett falsches Mindset für diese Tätigkeit> vorliegt.
Ohhh da war Dein Vorredner etwas Einfühlsamer!
... und nen schönen Garte habe ich, dafür soll es was werden, mit dem
Programm ;)
(zum Glück hab ich ein dickes Fell und kenn hier den rauhen Ton! Zum
Glück sind ja nicht alle wie Ihr :) )
MAT
> Ohhh da war Dein Vorredner etwas Einfühlsamer!> ... und nen schönen Garte habe ich, dafür soll es was werden, mit dem> Programm ;)> (zum Glück hab ich ein dickes Fell und kenn hier den rauhen Ton! Zum> Glück sind ja nicht alle wie Ihr :) )> MAT
Die perfekte Antwort! Gefällt mir!
Matthias S. schrieb:> Hat jemand dazu eine Idee?
Vermutlich (schließe ich aus deinen komma-Berechnungen) hast du den
Unterschied zwischen Zeigern, Referenzen, dem Speicherbereich, auf den
Zeiger oder Referenzen zeigen sowie den Datentypen size_t und int nicht
verstanden.
Genauer kann ich dir das nicht sagen, weil ich die von dir verwendeten
Datentypen nicht kenne, du zeigst sie uns ja nicht.
MfG, Arno
Hallo Matthias,
schön, das du den Weg hierher gefunden hast.
Leider wirst du bald feststellen, das sich in diesem Forum einige Typen
versammeln, die ein soziales und geistiges Handicap haben. Sie befinden
sich im Stadium unheilbar.
Ich bin sicher, dass dir mittelfristig geholfen wird.
Gruß DaMusstDuDurch
Arno schrieb:> Genauer kann ich dir das nicht sagen, weil ich die von dir verwendeten> Datentypen nicht kenne, du zeigst sie uns ja nicht.
Da hast Du recht, das war mein Versäumnis!
1
char*token;// token Bedeutung = Zeichen
2
intcount_[4]={0,0,0,0};
3
//char Tiken[11];
4
bytekomma[10];
5
byteanzahl;
6
charDaten_1[40];// Zwischenpuffer für das extrahierte Chararray ohne crc16-Daten
7
charDaten_2[40];// Zwischenpuffer für Zwischenspeicherung Chararray (z.Bsp.: crc16)
8
uint16_tcrc_1;
Also zurück zum Thema, wenn ich besser wüste, würde ich hier nicht
fragen!
An die "Überflieger" hier im Forum, ich habe diesen String, der über
UART reinkommt: [_A,index,2.80,182,]E6B1. Ich benötige die Werte
1. "index"
2. 2.80
3. 182
4. E6B1
wie würdet Ihr vorgehen, um an diese Variablen ranzukommen?
Matthias S. schrieb:> Hatte vergessen, dass ich noch blutiger Anfänger bin!
Das sehen wir!
Musst du also nicht sagen.
(Regen ist nass)
Matthias S. schrieb:> Ich wollte mich ersteinmal da durcharbeiten und dann mit den Funktionen> rantrauen.
Was du willst, scheint mit zu sein, Daten analysieren/interpretieren, um
dann irgendwas damit anzustellen.
Ich fasse das erste halbe Dutzend der notwendigen Schritte, auf dem Weg,
mal als "Parser" zusammen.
Der Parserbau ist eine wunderbare Gelegenheit, um sich mit endlichen
Automaten zu beschäftigen.
Schritt1:
Erstmal benötigt man eine glasklare Definition der eintrudelnden Daten.
Was ist Was!
Hier ist schon der erste Konflikt.
Matthias S. schrieb:> [_A,index,2.80,182,]E6B1Matthias S. schrieb:> ([_A/temp/,Index,246,]53ED
Einmal 3, einmal 4, in den eckigen Klammern.
Das wirft die Frage auf: Wie viele mag es da wohl geben?
96 Tausend?
EAF schrieb:> Was du willst, scheint mit zu sein, Daten analysieren/interpretieren, um> dann irgendwas damit anzustellen.
Das ist richtig! Ich habe auf der einen Seite einen ATmega2560 mit einem
Display 128x240. Auf dem werden Daten generiert, die über Uart auf einen
ESP geschickt werden sollen. Diese möchte ich dann auf einer Webseite
darstellen.
EAF schrieb:> Matthias S. schrieb:>> [_A,index,2.80,182,]E6B1>> Matthias S. schrieb:>> ([_A/temp/,Index,246,]53ED
Das sind unterschiedliche Ideen, diese Daten zusammen zustellen. Von
INteresse wäre:
- "index" (dies soll die Dateninfo für die Art/Bestimmung der Daten
sein; es sollen später noch weiter hinzukommen)
=> das wäre ein String oder Chararray (was wäre denn besser??)
- 2.80.... max. 10 Datensätze
=> das wären Byts, Single oder auch Chararrays
- 53ED = crc-16 Checksumme, die vom Sendesystem über die Daten in den
Klammern [...] errechnet wurde
=> das wäre eine Hex Int
Soviel zu den Daten.
Matthias S. schrieb:> Das sind unterschiedliche Ideen, diese Daten zusammen zustellen.
So so...
Das Protokoll ist nicht ausgereift!
Dabei ist die Reihenfolge doch offensichtlich!
Ohne fertiges Protokoll, keinen Parser.
Dann hier ein Tipp:
Verwende den CmdMessenger.
Evtl, etwas gewöhungsbedürftig, aber der Parser ist schon fertig.
EAF schrieb:> Das Protokoll ist nicht ausgereift!
Das wird so sein, weil ich es mir im Moment es so vorstelle, dass es so
laufen kann.
Dann sollte ich ersteinmal mich schlau machen, wie ein Protokoll
aufgebaut, "design" werden sollte.
EAF schrieb:> Dabei ist die Reihenfolge doch offensichtlich!
Wie meinst Du das?
EAF schrieb:> Verwende den CmdMessenger.
Das ist doch schon einmal ne Richtung! Danke!
Matthias S. schrieb:> EAF schrieb:>> Dabei ist die Reihenfolge doch offensichtlich!> Wie meinst Du das?
Ein Parser ist hochspezialisiert. In der Regel.
Die kleinste Änderung an den "Voraussetzungen" befördert ihn und alle
vorhergehende Arbeit in die Tonne.
Von daher: Erst das Protokoll, dann der Parser.
EAF schrieb:> on daher: Erst das Protokoll, dann der Parser.
OK! Dann brauche ich noch einwenig Hintergrundwissen.
Von meinem Sender möchte ich schicken:
1. Zeichenketten
2. byte-Variablen
3. Intergerwerte
4. Hexadezimalewerte
5.....
Mein Sendesystem habe ich mit Bascom programmiert und stell mich etwas
schwer mit c++.
In welcher Form bereite ich meine Daten auf, dass diese leicht zu
versenden sind? Also verschicke ich nur Strings, die ich am Ausgang und
dann wieder auf der Empfängerseite zurück gewandelt werden müssen?
Von meinem Sender möchte ich schicken:
1. Zeichenketten
2. byte-Variablen
3. Intergerwerte
4. Hexadezimalewerte
Matthias S. schrieb:> Also verschicke ich nur Strings, die ich am Ausgang und> dann wieder auf der Empfängerseite zurück gewandelt werden müssen?Matthias S. schrieb:> Mein Sendesystem habe ich mit Bascom programmiert
Du musst dir ein Protokoll in der Art aufbauen:
Rohdaten senden "#S(string)#B(byte)#I(integerwert)#H(hexdezimal)"
Empfänger sollte nun die ankommenden Zeichen einordnen können:
wenn #S dann die nächsten Zeichen bis zum nächsten # ein String
wenn #B dann die nächsten Zeichen bis zum nächsten # ein Byte
...
Matthias S. schrieb:> In welcher Form bereite ich meine Daten auf, dass diese leicht zu> versenden sind? Also verschicke ich nur Strings, die ich am Ausgang und> dann wieder auf der Empfängerseite zurück gewandelt werden müssen?
Am leichtesten für Computer sind immer Rohdaten vulgo Binärdaten. D.h.
keine Strings.
Du kannst den statischen Ansatz wählen: Feste Pakete mit fester
Struktur. Da weißt du genau an welcher Stelle welcher Datentyp steht und
wie lang der ist.
Das kann man dann sogar direkt mit einer C-Struktur abbilden.
Oder den dynamischen: z.B. TLV (Type-Length-Value). Da kannst du
beliebige Daten rein tun, musst aber jedes Datum mit Meta-Daten
anreichern. Nämlich Typ und Länge.
Cyblord -. schrieb:> Am leichtesten für Computer sind immer Rohdaten vulgo Binärdaten. D.h.> keine Strings.
Das geht solange gut, bis Systeme mit verschiedener Endianess
aufeinander treffen.
Dann steigt der Aufwand.
Matthias S. schrieb:> OK! Dann brauche ich noch einwenig Hintergrundwissen.> Von meinem Sender möchte ich schicken:> 1. Zeichenketten> 2. byte-Variablen> 3. Intergerwerte> 4. Hexadezimalewerte> 5.....
Sendet der einfach so drauf los, oder gibt es vorher eine Anfrage?
Die Werte stehen doch für irgendwas. Kannst denen nicht Namen/Tags
geben?
Dirk B. schrieb:> Sendet der einfach so drauf los, oder gibt es vorher eine Anfrage?
Das mache ich ja schon. Ich weiss was ich an welcher Stelle sende bzw.
empfange. Im Eingangspost zeigte ich meine Weise auf, wie und was ich
mit den Datenstring mache und dann wurden meine wackligen Gehversuche
"auseinander" genommen.
Im Grunde genommen bekomme ich schon meine Daten, nur wird mir
unerklärlich am Schluss eine Variable doppelt kopiert.
Hmmm, mache jetzt dai dai und morgen geht's weiter.
Gute Nacht.
Matthias S. schrieb:> Arno schrieb:>> Genauer kann ich dir das nicht sagen, weil ich die von dir verwendeten>> Datentypen nicht kenne, du zeigst sie uns ja nicht.>> Da hast Du recht, das war mein Versäumnis!>>
1
>char*token;
2
>// token Bedeutung = Zeichen
3
>intcount_[4]={0,0,0,0};
4
>//char Tiken[11];
5
>bytekomma[10];
6
>byteanzahl;
7
>charDaten_1[40];
8
>// Zwischenpuffer für das extrahierte Chararray ohne crc16-Daten
9
>charDaten_2[40];
10
>// Zwischenpuffer für Zwischenspeicherung Chararray (z.Bsp.: crc16)
11
>uint16_tcrc_1;
12
>
>
Damit ist zwar immer noch sehr konfus, vor allem mit Variablen-Namen wie
anzahl, komma, count_ und davon noch jeweils Arrays, aber so ein
bisschen verstehe ich, was da passieren soll.
Verdacht: Du kopierst anzahl Zeichen nach Daten1[], setzt dann aber
nicht das Zeichen an Position anzahl auf \0, sondern das Zeichen an
Position komma[count_[0] -1 ]. Und da vorher schon was in Daten1[]
drinstand, bleibt das da.
Tipp: Nach jedem Schritt alle Zwischenergebnisse (mit Zeilennummer)
ausgeben oder im Debugger anzeigen, dann solltest du selbst darauf
kommen.
Und mittelfristig, um solche Fehler nicht zu wiederholen: Viel, viel
weniger Chaos produzieren ;)
Du kannst zum Beispiel char_ser (oder eine Kopie davon) verändern, statt
immer wieder komma[n] = strlen(...) - komma[n-1] zu rechnen. Die
String-Funktionen haben auch Rückgabewerte, die man nutzen kann und
sollte. Es ist auch Quatsch, n+1 Zeichen zu kopieren, um dann das
Zeichen an Stelle n auf \0 zu setzen - das spart dir vermutlich ein paar
magic +1 und -1. Und dann kann es gut sein, dass du die ganze
Substring-Kopiererei viel besser in eine Funktion kapseln kannst, die du
mehrfach aufrufst. Wenn es nicht eh schon eine Standard-Funktion dafür
gibt.
Und dass das mit C++-String-Funktionen wahrscheinlich viel lesbarer ist
als mit C-char[]-Funktionen wurde ja auch schon erwähnt.
MfG, Arno
Dirk B. schrieb:> Das geht solange gut, bis Systeme mit verschiedener Endianess> aufeinander treffen.> Dann steigt der Aufwand.
Nicht wirklich. Das siehst du daran dass man für Netzwerkkommunikation
gerne die htonl, htons, ntohl, ntohs Funktionen verwendet. Und dann
spielt die Endianess plötzlich keine Rolle mehr.
Cyblord -. schrieb:> Und dann spielt die Endianess plötzlich keine Rolle mehr.
Auf Kosten einer dazwischen geschobenen Schicht.
Eine solche ist die Klartext/Ascii Schicht auch.
Zudem von Menschen lesbar.
Man kann sich über das "Maschine zu Maschine" Protokoll streiten.
Keine Frage.
Aber eindeutig, das muss es sein, dazu gibt es keine Alternative.
foobar schrieb:> Les dir mal die man-Page zu strncpy durch - sie macht nicht das,> was du> vermutest.
Deshalb auch mein Vorschlag mit std::string. Damit braucht man die
gefährlichen K&R Stringroutinen nicht. Selbst bei strncpy() muss man
evtl hinten noch ein '\0' einsetzen. Das kann ein Anfänger nicht wissen.
Deshalb sollte es die Finger davon lassen.
PittyJ schrieb:> Deshalb auch mein Vorschlag mit std::string. Damit braucht man die> gefährlichen K&R Stringroutinen nicht. Selbst bei strncpy() muss man> evtl hinten noch ein '\0' einsetzen. Das kann ein Anfänger nicht wissen.> Deshalb sollte es die Finger davon lassen.
Dann gib mir bitte ein Beispiel.
> evtl hinten noch ein '\0' einsetzen. Das kann ein Anfänger nicht wissen.> Deshalb sollte es die Finger davon lassen.
Wenn Du meinen Code angeschaut hast, habe ich schon daran gedacht,
wohlmöglich aber nicht richtig umgesetzt.
Wie gesagt, der Hauptteil vom Code funktioniert, auch die CRC Berechnung
mit Vergleich.
Ih versuch nun mal mich mit den von Dir vorgeschlagenen "std::string" zu
befassen.
foobar schrieb:> Les dir mal die man-Page zu strncpy durch - sie macht nicht das, was du> vermutest.
OK, was vermute ich denn?
Und was muss ich denn dabei beachten bzw. was läuft bei dem Code den
falsch?
> was vermute ich denn?
Ok, ich hab falsch vermutet, sorry. Dein Kode ist so unleserlich
(wirklich, als ob er durch einen Obfuscator gejagt wurde; die Spaces am
Zeilenende geben ihm den Rest), dass ich nur das Ende gelesen und den
typischen strncpy Fehler gesehen habe (ein String ist länger als
erwartet).
Du versucht, das Problem durch explizite "dst[len-1]=0" zu beheben, hast
dich dabei aber hier wohl verrannt:
1
> anzahl = komma[count_[0]-1] - komma[0];
2
>
3
> // # Daten zwischen den Kommas in Daten_1 kopieren
Arno schrieb:> Verdacht: Du kopierst anzahl Zeichen nach Daten1[], setzt dann aber> nicht das Zeichen an Position anzahl auf \0, sondern das Zeichen an> Position komma[count_[0] -1 ]. Und da vorher schon was in Daten1[]> drinstand, bleibt das da.
Hallo Arno,
vielen Dank für Deinen Beitrag! Dieser Teil hat mich auf die richtige
Spur gebracht und nach suchen/lesen zur Funktion "strncpy" hab ich diese
verstanden.
Der Fehler war, dass ich die Null Terminierung nicht richtig gesetzt
hatte.
Jetzt geht es!
Warum nicht die "überholten" C-Stringfunktionen benutzen?, Geht doch ;)
Arno schrieb:> Tipp: Nach jedem Schritt alle Zwischenergebnisse (mit Zeilennummer)> ausgeben oder im Debugger anzeigen, dann solltest du selbst darauf> kommen.
Debuggen in Arduino? Wie geht das. Über kurz oder lnag werd ich wohl als
IDE auf Visual Studio Code umsteigen, da sollte es ja funktionieren...
Oder hast Du einen anderen Vorschlag?
Arno schrieb:> Und mittelfristig, um solche Fehler nicht zu wiederholen: Viel, viel> weniger Chaos produzieren ;)
gib mir bitte ein Beispiel, was Deiner Ansicht Chaos ist und zeige mir,
wie es besser sein könnte. Will ja gern lernen :)
Arno schrieb:> Die> String-Funktionen haben auch Rückgabewerte, die man nutzen kann und> sollte.
Wie sieht denn der Rückgabewert bei "strncpy" aus?
Grundsätzlich würde mich jedoch interessieren wie ein "Profi" den String
auseinander nehmen würde...
[_A,index,2.90,221,37.29,34.78,41.76,]647B
Brauche die Daten zwischen den Kommas :)
Vielen DAnk nochmals :)
foobar schrieb:> Ok, ich hab falsch vermutet, sorry. Dein Kode ist so unleserlich> (wirklich, als ob er durch einen Obfuscator gejagt wurde; die Spaces am> Zeilenende geben ihm den Rest), dass ich nur das Ende gelesen und den> typischen strncpy Fehler gesehen habe (ein String ist länger als> erwartet).
Hi foobar!
Danke für die Antwort!
ich räume den Code jetzt weiter auf. Inzwischen habe ich meinen Fehler
gefunden, habe die ganze Zeit gedacht, dass ich die Nullterminierung
richtig gesetzt hatte. Da lag mein Fehler!
1
anzahl=komma[count_-1]-komma[0];
2
strncpy(Daten_1,chararray_ser+komma[0],anzahl);
3
Daten_1[anzahl]='\0';
foobar schrieb:> Dein Kode ist so unleserlich> (wirklich, als ob er durch einen Obfuscator gejagt wurde;
Das mag für ein Profi so sein ;)
Ich hatte mir einen Kopf gemacht, wie ich aus einem Chararray, unter zu
Hilfenahme der str-Funktionen, die gewünschten Variablen
herausbekomme....
> Grundsätzlich würde mich jedoch interessieren wie ein "Profi" den> String auseinander nehmen würde...
Wenn der String in einem beschreibbaren Puffer liegt, würde ich gar
nichts kopieren - im Prinzip die Komma durch \0 ersetzen und die
Positionen merken
Z.B. so:
1
#define NELEM(x) (sizeof(x) / sizeof(*(x)))
2
3
// split str at delim (in place)
4
// stores substrings in res[], returns nr of delim found
foobar schrieb:> Z.B. so:
Wau... Auf so etwas würde ich nie kommen. Das muss ich erst einmal
durchgehen und versuchen zu verstehen! Aber ich bin noch lange nicht
soweit das zu verstehen, dazu bräuchte ich einen Telefon Joker ;)
Das ist nicht mein Handwerk, mache lieber meinen Garten schön :)
(bezogen auf den Anfangspost)
Danke für Deine Mühen!
MAT
Matthias S. schrieb:> Grundsätzlich würde mich jedoch interessieren wie ein "Profi" den String> auseinander nehmen würde...> [_A,index,2.90,221,37.29,34.78,41.76,]647B> Brauche die Daten zwischen den Kommas :)
Wenn du statt Kommas Whitespace (Leerzeichen oder Tabulator) nehmen
würdest, kannst da das ganz einfach mit sscanf machen.
sscanf(buffer,“[%s %s %s %s %s %s %s ] %s“, …)
mit Komma
sscanf(buffer,“[%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],] %s“, …)
Für die … kommen 7 Adressen von char-Arrays.
Du kannst die Werte auch gleich im richtigen Format (%f %x %d) lesen.
Dirk B. schrieb:> Wenn du statt Kommas Whitespace (Leerzeichen oder Tabulator) nehmen> würdest, kannst da das ganz einfach mit sscanf machen.
Super!
Mein erdachtes Protokoll kann ich jederzeit ändern!
Es gibt unendliche Möglichkeiten, wie soll man darauf als Laie kommen,
Danke für die Hilfen hier!
Matthias S. schrieb:> Dirk B. schrieb:>> Wenn du statt Kommas Whitespace (Leerzeichen oder Tabulator) nehmen>> würdest, kannst da das ganz einfach mit sscanf machen.>> Super!
Die meisten Formatspecifier von scanf überlesen Whitespace am Anfang und
hören auf, wenn kein passendes Zeichen mehr gefunden wurde.
Ein Whitespace im Formatstring steht für beliebig viele Whitespace im
Text.
Dabei ist es egal, welches Whitespace (Leerzeichen ' ', Tabulatoren '\t'
'\f', Zeilenvorschub '\n', Wagenrücklauf '\r') ist.
Das erste Beispiel läßt sich auch auf sscanf(buffer,“[%s%s%s%s%s%s%s
]%s“, …)
kürzen.
scanf ist eine komplexe Funktion und damit recht teuer (Speicherplatz,
Laufzeit)
Dirk B. schrieb:> scanf ist eine komplexe Funktion und damit recht teuer (Speicherplatz,> Laufzeit)
Das ist ein guter Hinweis, ich arbeite hier auf einem ESP und bin noch
nicht richtig konfirm, was darauf alles möglich ist.
Ich würde diesen Parser auf einem PC entwickeln, testen und debuggen.
Erst danach ins Arduino Projekt einfügen.
> Mein erdachtes Protokoll kann ich jederzeit ändern!
Dann mache das. Etwas Text basiertes mit Label ist viel einfacher zu
parsen:
1
raum:Wohnzimmer
2
temp_soll:21.5
3
temp_ist:21.0
4
fenster:offen
5
tuere:geschlossen
Eine Leerzeile danach kennzeichnet das Ende des Datensatzes. So
funktioniert übrigens das allgegenwärtige HTTP Protokoll.
> Blödsinn! Der TO hat noch ganz andere Probleme als Rekursionen.
Dirk hat schon recht: Wenn strtok, dann strtok_r. Hat mit Rekursion
nichts zu tun, einfach nur verschachtelte strtok-Aufrufe.
> sscanf(buffer,“[%s %s %s %s %s %s %s ] %s“, …)
Das Problem bei sscanf steckt in den "…": Jedes "%s" bzw "%[...]"
braucht einen eigenen Buffer, der mind so groß wie "buffer" ist - das
wird, insb auf Mikrocontrollern mit wenig RAM, eklig.
> Arduino ist C++. Da geht auch std::string
Sicher, dass Arduino std::string hat? Ich hatte den Eindruck, das
gibt's da nicht. Selbst wenn: dynamische Speicherverwaltung (wie u.a.
von std:string verwendet) ist auf Mikrocontrollern problematisch (wenig
RAM, Fragmentierung, OOM, ...). Wenn nicht unbedingt nötig, vermeiden.
foobar schrieb:> Dirk hat schon recht: Wenn strtok, dann strtok_r. Hat mit Rekursion> nichts zu tun, einfach nur verschachtelte strtok-Aufrufe.
Ach?
Genau DAS nennt man dann Rekursion.
Zitat aus der Man-Page:
> The strtok_r() function is a reentrant version of strtok().
Sowas braucht man u.A. bei Rekursionen, aber ganz sicher nicht in dem
hier beschrieben Scenario.
W.g.: der TO kämpft derzeit noch viel simpleren Problemen.
Für strtol_r gibt es hier absolut keinen Grund.
>> Dirk hat schon recht: Wenn strtok, dann strtok_r. Hat mit Rekursion>> nichts zu tun, einfach nur verschachtelte strtok-Aufrufe.>> Ach? Genau DAS nennt man dann Rekursion.
Nein, zwei verschachtelte Schleifen sind noch keine Rekursion.
Lass das mal ohne und mit dem "#define" laufen (also einmal mit strtok_r
und einmal mit strtok). Bei strtok ist nach dem ersten "word"
Feierabend, die folgenden Wörter werden nicht mehr behandelt. Besonders
lustig wird's, wenn die innere Schleife in einer Unterroutine steckt und
man gar nicht erwartet hat, dass die strtok benutzt. Analog: wenn der
Aufrufer von test auch zufälligerweise strtok nutzt, zerhaut's den.
An sich gehört strtok aus dem Standardlibrary rausgeschmissen - nur der
Kompatibilität wegen gibt's das noch.
Harry L. schrieb:> Der TO hat noch ganz andere Probleme als Rekursionen.
Das _r steht nicht für "rekursiv", sondern für "reentrant".
Harry L. schrieb:> foobar schrieb:>> Dirk hat schon recht: Wenn strtok, dann strtok_r. Hat mit Rekursion>> nichts zu tun, einfach nur verschachtelte strtok-Aufrufe.>> Ach?> Genau DAS nennt man dann Rekursion.
Rekursion ist, wenn etwas (direkt oder indirekt) sich selbst aufruft.
Das macht strtok nicht.
Matthias S. schrieb:> Dirk B. schrieb:>> scanf ist eine komplexe Funktion und damit recht teuer (Speicherplatz,>> Laufzeit)>> Das ist ein guter Hinweis, ich arbeite hier auf einem ESP und bin noch> nicht richtig konfirm, was darauf alles möglich ist.
Ich setze noch einen obenauf.
Um die Denke weiter zu trainieren, hilft es m.M.n. weiter weg von der
kleinteiligen uC-Welt zu gehen, erst danach wieder auf
RAM-&MHz-limitierte implementation zurückzukommen.
Ich mag PCRE, insbesondere in der Pythonimplementation welche direkt
Dictionary zurückliefert.
Als Übung empfehle ich mal den NMEA Datenstrom eines
Navis/GPS-Rx(strings!) per (USB)seriellen schnittstelle am PC einzulesen
und und ein paar Datenfelder rauszupicken.
Bei geschicktem vorgehen (wenn der Groschen fällt) ergibt sich ein klar
erkennbares generisches Muster an PCRE, welches sich dann einfach auf
weitere Datenfelder ausbauen lässt und zwar OHNE weiteren Programmcode
schreiben zu müssen sondern eben bloss weitere PCRE-Schnipsel
hinzuzufügen.
Das lässt sich dann natürlich nicht auf "Arduino stufe" runterbrechen
(C, C++, stdlib, mit/ohne sscanf, ...) aber lichtet das Chaos im kopp
und gibt einem eine Vision/Leitfaden wie Protokoll und dessen Behandlung
aussehen könnte.
Mit gegebenen Einschränkungen der Zielplattform/Umgebung kann man dann
den sinnvoll nötigen minimalteil davon schon umsetzen.
Dieser Input hier von mir will auch eine gebrochene Lanze dafür sein,
dass eine zusätzliche ganz andere Programmiersprache (i.deren Libs) als
jene welche man gerade verwenden will/muss eine sehr Grosse und
nützliche Bereicherung ist.
Harry L. schrieb:>> Nein.>> Wenn strtok_r>> Blödsinn!> Der TO hat noch ganz andere Probleme als Rekursionen.
Liebes Forum,
coole Sache Eure Hilfe!
strtok_r ist schon ganz gut und habe ich auch in Benutzung.
foobar schrieb:> Dirk hat schon recht: Wenn strtok, dann strtok_r. Hat mit Rekursion> nichts zu tun, einfach nur verschachtelte strtok-Aufrufe.
Jup so ist es! Strtok ist nicht wiedereintrittsfähig, das heisst, ich
kann keine weiter Suchdurchläufe in Verschachtelung betreiben. Soviel
habe ich auch im Netz mir erarbeitet.
foobar schrieb:>> sscanf(buffer,“[%s %s %s %s %s %s %s ] %s“, …)>> Das Problem bei sscanf steckt in den "…": Jedes "%s" bzw "%[...]"> braucht einen eigenen Buffer, der mind so groß wie "buffer" ist - das> wird, insb auf Mikrocontrollern mit wenig RAM, eklig.
Genau das ist es, ich habe ja geschrieben, dass mein Zielsystem ein
ESP8266 ist. Und alles gut und schön mit den Funktionen unter C++, nur
wenn ein Anfänger nicht weiß, was darin vorgeht oder es an Recourcen
benötigt, handelt man sich schnell Probleme ein, die größer sind als die
schon vorhandene Unwissenheit :)
foobar schrieb:>> Arduino ist C++. Da geht auch std::string>> Sicher, dass Arduino std::string hat? Ich hatte den Eindruck, das> gibt's da nicht. Selbst wenn: dynamische Speicherverwaltung (wie u.a.> von std:string verwendet) ist auf Mikrocontrollern problematisch (wenig> RAM, Fragmentierung, OOM, ...). Wenn nicht unbedingt nötig, vermeiden.
Jup, dass habe ich mir dann im Netz auch erlesen und das ist das gleiche
Problem siehe sscanf.......
foobar Du hast es auf den Punkt gebracht! Danke.
Harry L. schrieb:> W.g.: der TO kämpft derzeit noch viel simpleren Problemen.> Für strtol_r gibt es hier absolut keinen Grund.
Mist, habt mich erwischt ;)
Strtok_r ist schon richtig gut, so könnte man den String nach
unterschiedlichen Trennzeichen auseinander nehmen, bin aber noch nicht
darauf gekommen wie, hab da noch einen Knoten im Kopf.
So ich werde mal weiter machen, mein Eingangsproblem habe ich gelöst und
dabei gelernt! Sicherlich ist das alles noch nicht optimal aber das wird
schon noch (hoffe ich :) ).
Matthias S. (mat-sche)
18.02.2022 17:19
>sitze hier schon seit tagen drann und komm nicht weiter.>Ich habe einen String : [_A,index,2.80,182,]E6B1>Ich benötige in einzellnen Variablen: ,index,2.80,182,
Ich nutzte gerne das Teilen eines Strings in zwei Hälften.
Wie wäre es damit?:
1
voidsetup()
2
{
3
Serial.begin(115200);
4
}
5
6
StringrightString(Strings)
7
{
8
Strings2=s.substring(s.indexOf(',')+1);// right string
9
returns2;
10
}
11
12
StringleftString(Strings)
13
{
14
Strings2=s.substring(0,s.indexOf(','));// left string
> Ich nutzte gerne das Teilen eines Strings in zwei Hälften.> Wie wäre es damit?:>>
1
>StringrightString(Strings)
2
>{
3
>Strings2=s.substring(s.indexOf(',')+1);// right string
4
>returns2;
5
>}
6
>
7
>StringleftString(Strings)
8
>{
9
>Strings2=s.substring(0,s.indexOf(','));// left string
10
>returns2;
11
>}
12
>
Ein Zwischenschritt um im Kopf die Ideen zu ordnen, ja.
Aber ausgerechnet auf einer limitierten Zielplattform immer wieder die
selbe Position desselben Trennzeichens ',' zu ermitteln und wieder zu
vergessen (lokale Variable) um es in der nächsten Funktion abermals zu
ermitteln... Ist halt "wer es nicht im Kopf hat, hat es in den Beinen".
Auch: wie verhalten sich als lokale Variablen angelegte
String-Instanzen? Und was passiert damit wenn als rückgabewert der
Funktion gebraucht? (das muss nicht falsch sein, aber gut zu wissen wie
sich das auswirkt...)
Dann lieber die Positionen der nächsten z.B. 7 (wieviele werden es
maximal? Auch in zukunft?) Trennzeichen ermitteln und damit
rumschnippeln, Idealerweise ohne ständig Teilstrings rumzukopieren.
Weil: limitierte Zielplattform.
Stefan ⛄ F. schrieb:> Dann mache das. Etwas Text basiertes mit Label ist viel einfacher zu> parsen:> raum:Wohnzimmer> temp_soll:21.5> temp_ist:21.0> fenster:offen> tuere:geschlossen
Dann wenigstens gleich JSON, dafür gibts fertige Parser für C.
foobar schrieb:> Wenn der String in einem beschreibbaren Puffer liegt, würde ich gar> nichts kopieren - im Prinzip die Komma durch \0 ersetzen und die> Positionen merken
Das mache ich ähnlich. Allerdings sieht meine split-Funktion anders aus
und behandelt nur das jeweils nächste Trennzeichen. Dadurch wird das
Array überflüssig.
1
char*split(char*s,charc){
2
while(*s!=c&&*s!=0)s++;
3
if(*s==c)*s++=0;
4
returns;
5
}
Zurückgegeben wird ein Zeiger auf den Rest des Strings oder auf die
Endnull, wenn kein Trennzeichen gefunden wurde.
Das Parsen würde etwa so aussehen:
Jobst Q. schrieb:> Das mache ich ähnlich. Allerdings sieht meine split-Funktion anders aus> und behandelt nur das jeweils nächste Trennzeichen. Dadurch wird das> Array überflüssig.
Ich versuch gerad zu verstehen, wie ich dies für meinen String anwenden
kann. Kannst Du mir bitte dies an
[_A,index,2.90,221,37.29,34.78,41.76,]647B verdeutlichen?
da aus wo ich gucke schrieb:> Ist halt "wer es nicht im Kopf hat, hat es in den Beinen".
Ja schon, andererseits ist der Code so leichter lesbar. Wenn die
Performane an dieser Stelle unkritisch ist, würde ich die besser lesbare
Version bevorzugen.
Manchmal leistet der Optimizer des C Compiler aber auch erstaunliches.
Zum Beispiel wird hier die teure Division nicht zweimal durchgeführt,
obwohl es der Quelltext suggeriert.
da aus wo ich gucke (Gast)
>Auch: wie verhalten sich als lokale Variablen angelegte>String-Instanzen? Und was passiert damit wenn als rückgabewert der>Funktion gebraucht? (das muss nicht falsch sein, aber gut zu wissen wie>sich das auswirkt...)
Es hindert dich niemand daran, dass herauszufinden.
>Dann lieber die Positionen der nächsten z.B. 7 (wieviele werden es>maximal? Auch in zukunft?) Trennzeichen ermitteln und damit>rumschnippeln, Idealerweise ohne ständig Teilstrings rumzukopieren.>Weil: limitierte Zielplattform.
Jede Platform ist limitiert und der hier angestrebte ES8266 ist es schon
weniger als ein Atmega328.
Aber da mir meine erste Lösung auch nicht so gute gefallen hatte, hier
die nächste:
Christoph M. schrieb:> Aber da mir meine erste Lösung auch nicht so gute gefallen hatte, hier> die nächste:
Das nenne ich mal einfach und gut durchdacht! Habe auf der Arduino
Webseite dier : Reference > Language > Variables > Data types > String >
Functions >
gefunden.... Die hilft auch weiter.
Dein Beispiel werd ich wohl mir näher ansehen und implementieren.
Danke!
>Das nenne ich mal einfach und gut durchdacht!
Ah, danke für's Lob.
Es lohnt sich, die String Funktionen mal genau anzusehen:
https://www.arduino.cc/reference/de/language/variables/data-types/stringobject/
Noch ein kleiner Hinweis: beim obigen Code wird für "index" immer das
Wort index geholt. Möglicherweise willst Du aber das "_A" statt dem Word
"index". An der Stelle müsste der Code dann noch angepasst werden.
Dirk B. schrieb:> Jobst Q. schrieb:>> strdup(s);>> strdup?>> Echt jetzt?
War nur als Beispiel für längere Weiterverwendung des Strings. Solange
keine neuen Daten kommen, reicht auch svar1=s;
Ich benutze es eigentlich so gut wie nie, da es per malloc arbeitet und
später wieder ein free() erfordert. Für Mikrocontroller ist es schon
garnicht zu empfehlen.
Matthias S. schrieb:> Jobst Q. schrieb:>> Das mache ich ähnlich. Allerdings sieht meine split-Funktion anders aus>> und behandelt nur das jeweils nächste Trennzeichen. Dadurch wird das>> Array überflüssig.>> Ich versuch gerad zu verstehen, wie ich dies für meinen String anwenden> kann. Kannst Du mir bitte dies an> [_A,index,2.90,221,37.29,34.78,41.76,]647B verdeutlichen?
In meinem Beispiel zum Parsen habe ich doch schon die ersten 4 Felder
deines ersten Postings im Kommentar. Du müsstest es nur noch um case 5
und 6 erweitern. Bei noch mehr Feldern entsprechend mehr cases.
Am Anfang kannst du den CRC-String auch mit split abtrennen:
s_crc= split (s,']'); Um das erste [ zu entfernen reicht s++;