unregelmässigen "CSV-String" parsen

Hi Leute,

heut bräuchte ich mal wieder einen Denkanstoss:
Folgender Fall:
ich bekomme über die serielle Schnittstelle einen ASCII-String. In dem String sind x Werte durch Komma getrennt.
Wenn ein Wert nicht existiert, dann steht da auch nichts drin, sondern es kommt gleich das nächste Komma.
Das heisst, daß ich die Datenwerte in meinem In-String nicht immer an den selben Stellen auslesen kann.

Das einzige was mir jetzt einfällt, ist, dass man beim Einlesen schaut, ob nach einem Komma das nächste Zeichen wieder ein Komma ist, und dann den Wert mit "Null" abspeichert.
Oder habt ihr da eine schlauere oder elegantere Idee, wie man das programmieren kann? Dazu kommt noch, daß die Werte, abhängig von der Größe, 1-3 Zeichen lang sein können.

Bsp.1: $3,2,,,,,,,,00,,,,,18,,,<\r>
Bsp.2: $3,2,09,26,30,156,36,12,26,231,00,28,25,054,44,18,21,277,00<\r>

gruß/hk007

Wander einfach durch den String und zähle die Kommas und speichere deren Position im String in einem Array (z.B. byte kommas[20]).
Wenn Du den 5. Wert haben willst, ist das dann der Teilstring von Position (kommas[5-2]+1) bis Position (kommas[5-1]-1).
Das kann man z.B. super in eine einzelne Funktion giessen.

hi,

sollte das nicht mit split(";") funktionieren?

gruß stefan

Sind doch alles Zahlen, oder ?

Wenn ja, dann wandele sie gleich, während du die Kommas suchst:

char datastring = "$3,2,,,,,,,,00,,,,,18,,,\r";
char * p = datastring+1; // nach dem '

Wenn du auch negative Zahlen hast, musst du ein bisschen was ergänzen :wink:

Ich hoffe, "$0,0,000,0" ist das gleiche wie "$,,,,"
sonst musst du natürlich was anderes machen.

int val=0;  // temp variable

int dataArray[20];
int i = 0;  // Index auf dataArray

while ( *p != '\r' && *p != 0)
{
  if (*p == ',')  // Zahl fertig
  {
      dataArray[i++] = val;
      val = 0;
  }
  else if ( *p >= '0' && p <= '9' )
      val = val
10 + *p - '0'; // einfachste ascii -> int Wandlung
}
    dataArray[i] = val; // den letzten Wert nicht vergessen


Wenn du auch negative Zahlen hast, musst du ein bisschen was ergänzen ;)

Ich hoffe, "$0,0,000,0" ist das gleiche wie "$,,,," 
sonst musst du natürlich was anderes machen.

HI,

danke an alle Vorschläge.

Ich hab mir das von michael_x mal genauer angeschaut.

So auf Anhieb krieg ichs nicht hin:
Ein paar Sachen hab ich mal ergänzt, damit man es kompilieren kann:

//char datastring = "$33,22,11,4,5,6,,,,00,,,,,18,,,\r"; ---> geändert
char datastring[] = "$33,22,11,4,5,6,,,,00,,,,,18,,,\r";
char *p = datastring + 1; // nach dem '

Wenn ich es laufen lasse, dann gibt das Terminal folgendes aus:

erster Durchlauf:
22
11
4
5
6
0
0
zweiter Durchlauf:
$22
11
4
5
6
0
0

Das sieht so weit gut aus. Nur die erste Zahl fehlt mir immer. Und ich schlepp aus dem alten Lauf was mit.
Das finde ich schon noch raus.
Aber ich verstehe nur eine Zeile nicht so ganz:
char *p = datastring + 1; // nach dem '$'
Was ist jetzt "p"? Ist das der Pointer auf das Zeichen im Datastring, oder ist es das Zeichen selber?? Da fehlts mir etwas an "C-Kenntnissen"

gruß/hk007

int val=0;  // temp variable
int dataArray[20];
int i = 0;  // Index auf dataArray

while ( *p != '\r' && *p != 0)
{
  if (*p == ',')  // Zahl fertig
  {
    dataArray[i++] = val;
      val = 0;
  }
  else if ( *p >= '0' && p <= '9' )
      val = val
10 + *p - '0'; // einfachste ascii -> int Wandlung
      p++; ---------------------> eingefügt
}
    dataArray[i] = val; // den letzten Wert nicht vergessen

Serial.println (dataArray[1]);
Serial.println (dataArray[2]);
Serial.println (dataArray[3]);
Serial.println (dataArray[4]);
Serial.println (dataArray[5]);
Serial.println (dataArray[6]);
Serial.println (dataArray[7]);


Wenn ich es laufen lasse, dann gibt das Terminal folgendes aus:

§DISCOURSE_HOISTED_CODE_1§


Das sieht so weit gut aus. Nur die erste Zahl fehlt mir immer. Und ich schlepp aus dem alten Lauf was mit.
Das finde ich schon noch raus. 
Aber ich verstehe nur eine Zeile nicht so ganz:
char *p = datastring + 1; // nach dem '$'
Was ist jetzt "p"? Ist das der Pointer auf das Zeichen im Datastring, oder ist es das Zeichen selber?? Da fehlts mir etwas an "C-Kenntnissen"

gruß/hk007

Oh ich Esel,

Ich muss natürlich bei den println-Befehlen auch das array[0] mit anzeigen.
Gut, wenn das mit drinsteht, dann sieht es so aus:

erster Durchlauf:
33
22
11
4
5
6
0
0
zweiter Durchlauf:
$33
22
11
4
5
6
0
0
dritter Durchlauf:
G33
22
11
4
5
6
0
0

Also ich würde für sowas einen "Stream Parser" bauen. Wie das geht? Morgen 00:01 ist der Beitrag Flexible Sweep | Blinkenlight öffentlich. Da trete ich das Problem mal so richtig breit :wink:

Sorry für meinen Tipp-Fehler:
char datastring[] ist natürlich richtig

char * p = datastring; heisst: p ist ein pointer auf datastring. p+1 ( oder datastring+1 ) zeigt auf das 2. Zeichen p++ erhöht den pointer.
( sorry nochmal )

*p ist dann vom Datentyp char => das Zeichen auf das p zeigt.

Ist doch ganz einfach :wink:

michael_x:
Sorry für meinen Tipp-Fehler:

Musst dich doch nicht entschuldigen. Bin um jedes Codeschnitzel froh :slight_smile:
Das mit dem zusäzlichen Zeichen war mein Fehler. Hatte in der Void loop() noch eine Codeleiche, die reingespukt hat. :disappointed_relieved:
Läuft jetzt.

Aber ich werd mir die Idee von mkl0815 auch noch mal anschauen.

gruß/hk007

Mein neuestes Experiment ist jetzt draussen. Du kannst den Parser von dort einfach übernehmen. Du musst für Deinen Anwendungsfall nur bei

    const char terminator[] = " \t\n\r";

den Tab durch das Komma ersetzen, und newline wegnehmen, also

    const char terminator[] = " ,\r";

und weiterhin max_fractional_digits auf 0 setzen.