Forum: Mikrocontroller und Digitale Elektronik String zerlegen funktioniert nicht richtig, 2-dimensionales Array


von Michl (Gast)


Lesenswert?

Hallo Leute,
folgendes Problem: Ich bekommen einen Pointer auf das erste Element 
eines Strings übergeben. Dieser String ist informell durch Kommas und 
Sterne unterteilt.
Nun versuche ich die jeweiligen Teilstrings mit "strtok" zu zerlegen und 
in einem zwei-dimensionalen Array zu speichern.
Leider funktioniert es nicht so wie ich will.
1
void store_irgendwas (char* ptr_source)
2
{
3
  char Datafield[20][10];
4
  char* ptr_Datafield;
5
  unsigned char counter;
6
7
  counter = 0;
8
9
  strtok (ptr_source, ",*");
10
  while (ptr_Datafield != NULL)
11
  {
12
    ptr_Datafield = &Datafield[counter][0];
13
    ptr_Datafield = strtok(NULL, ",*");
14
    counter++;
15
  }
16
  
17
18
  while(counter)
19
  {
20
    UART2_Transmit((unsigned char*) &Datafield[counter][0], 10);
21
    counter--;
22
  }
23
}

Ich weiß nur nicht warum... denke es liegt irgendwo an dem Zusammenspiel 
zwischen dem Pointer und "strtok".

Vielen Dank schonmal im Voraus.

von Walter (Gast)


Lesenswert?

ein Fehler ist schon Mal in
    UART2_Transmit((unsigned char*) &Datafield[counter][0], 10);
sollte
    UART2_Transmit((unsigned char*) &Datafield[counter-1][0], 10);
heißen!

von Walter (Gast)


Lesenswert?

einen hab ich noch:
ptr_Datafield = strtok (ptr_source, ",*");
statt
strtok (ptr_source, ",*");

von Michl (Gast)


Lesenswert?

Ne das erste kann verworfen werden, das is ok.
Das mit dem counter wieder um 1 dekrementieren ist wohl wahr^^ nichts 
desto trotz werden die anderen Strings falsch ausgegeben

von Klaus W. (mfgkw)


Lesenswert?

@Walter:
nee, das stimmt schon.
Ab dem zweiten Aufruf gibt man strtok doch NULL als
Zeiger, dann läuft es intern weiter.

Mir fehlt so ein bißchen eine vernünftige Fehlerbeschreibung...

von Michl (Gast)


Lesenswert?

Es sieht so aus, also ob der von strtok zurückgegebene String (bzw. die 
Adresse) nicht in meinem Array gespeichert wird.
Wobei es mir solangsam dämmert. Bitte korriegiert mich, wenn ich falsch 
liege:

Der aufruf mit strtok an sich stimmt. Allerdings gibt strtok einen 
Pointer auf die Anfangsadresse des ermittelten Strings zurück. Daher 
funktioniert das Speichern in meinen 2D-Array nicht. Vielmehr müsste ich 
manuell mit strncpy arbeiten...
In etwa so:
1
void store_nmea (unsigned char nmea_msg_id, char* ptr_source)
2
{
3
  char Datafield[20][11];
4
  char strlen_Datafield;
5
  char* ptr_Datafield;
6
  unsigned char counter;
7
8
  counter = 0;
9
10
11
  strtok (ptr_source, ",*");
12
  while (ptr_Datafield != NULL)
13
  {
14
    ptr_Datafield = strtok(NULL, ",*");
15
    strlen_Datafield = strlen(ptr_Datafield);
16
    strncpy ((char*)&(Datafield[counter][0]), ptr_Datafield, strlen_Datafield);
17
    Datafield[counter][strlen_Datafield] = 0x00;
18
    UART2_Transmit((unsigned char*)&Datafield[counter][0], strlen(Datafield[counter]));
19
    counter++;
20
  }
21
  
22
/*  while(counter)
23
  {
24
    UART2_Transmit((unsigned char*) &Datafield[counter][0], 10);
25
    counter--;
26
  }*/
27
}

von Klaus W. (mfgkw)


Lesenswert?

Genau!

Mit der Originalversion:
1
    ptr_Datafield = &Datafield[counter][0];
2
    ptr_Datafield = strtok(NULL, ",*");
wird nur der Zeiger ptr_Datafield erst auf ein Array-Element
gerichtet, dann gleich wieder in den zerlegten String.
Letztlich wird kein String in das Array kopiert.

von Karl H. (kbuchegg)


Lesenswert?

Du brauchst gar kein 2D Array.
Du brauchst ein Array von Pointern in welche du die Pointer, die dir 
strtok liefert, abspeicherst.
Jeder Pointer zeigt dann auf den Anfang eines Strings.

von Walter (Gast)


Lesenswert?

den hab ich immer noch oder sollte ich über Nacht erblindet sein:
ptr_Datafield = strtok (ptr_source, ",*");
statt
strtok (ptr_source, ",*");

ptr_Datafield ist doch nicht initialisiert, da müsste doch auch der 
Compiler eine Warnung bringen!

von Karl H. (kbuchegg)


Lesenswert?

Walter schrieb:
> den hab ich immer noch oder sollte ich über Nacht erblindet sein:
> ptr_Datafield = strtok (ptr_source, ",*");
> statt
> strtok (ptr_source, ",*");
>

Du hast nur einen Pointer!
Du brauchst aber ein Array von Pointern!
1
void store_irgendwas (char* ptr_source)
2
{
3
  char* ptr_Datafield[20];
4
  unsigned char counter;
5
6
  counter = 0;
7
8
  ptr_Datafield[counter] = strtok (ptr_source, ",*");
9
  while (ptr_Datafield[counter] != NULL)
10
  {
11
    counter++;
12
    ptr_Datafield[counter] = strtok(NULL, ",*");
13
  }
14
  
15
16
  while(counter)
17
  {
18
    UART2_Transmit((unsigned char*)ptr_Datafield[counter], 10);
19
    counter--;
20
  }
21
}

und denk drann, dass dir strtok den Originalstring zerstört

von Nick M. (Gast)


Lesenswert?

> Jeder Pointer zeigt dann auf den Anfang eines Strings.

Ohne es ausprobiert zu haben, aber ich glaub das geht nicht. 
Vermuteterweise zeigt danach der Pointer auf den Anfang eines tokens 
incl. dem kompletten Reststring.
Man muss also den Teilstring kopieren, denn strtok ist auf den 
Eingabestring nicht destruktiv.


Ansonsten kann ich nur das Buch "C in a Nutshell" v. O'Reilly empfehlen. 
Da wird sowas kompakt beschrieben.


Gruß,
Nick

von Michl (Gast)


Lesenswert?

Klar das würde auch gehn. Aber dann müste ich doch mit "malloc" Speicher 
allokieren, damit mir die Strings nicht irgendwo im Speicher stehn. 
Zumal ich strtok ja immer wieder aufrufe.
Der Quellstring ändert sich auch immer, auch in seiner Länge sowie der 
Länge der jeweiligen Teistrings.
Mit der obigen Variante geh ich dabei schon eher auf Nummer Sicher.

von P. S. (Gast)


Lesenswert?

Walter schrieb:

> den hab ich immer noch oder sollte ich über Nacht erblindet sein:

Keine Angst, du hast schon recht. Vor Allem mit der Warning - leider 
scheinen gerade Anfaenger Warnings keinerlei Bedeutung beizumessen :-/

von Karl H. (kbuchegg)


Lesenswert?

Nick Müller schrieb:
>> Jeder Pointer zeigt dann auf den Anfang eines Strings.
>
> Ohne es ausprobiert zu haben, aber ich glaub das geht nicht.
> Vermuteterweise zeigt danach der Pointer auf den Anfang eines tokens
> incl. dem kompletten Reststring.

Nein tut es nicht.

> Man muss also den Teilstring kopieren, denn strtok ist auf den
> Eingabestring nicht destruktiv.

Doch, ist es.


Bitte, lest die Doku zu strtok.

strtok manipuliert den Originalstring, indem es an den Trennungsstellen 
(die durch das Delimiter Token gegeben sind) '\0' einfügt!

Aus
  "Hallo World"

macht strtok (mit einem Blank als Delimiter

   "Hallo\0World"

Beim ersten Aufruf von strtok kriegt man einen Pointer auf das 'H' 
zurück (und gleichzeitig wird das \0 eingefügt). Beim nächsten Aufruf 
kriegt man einen Pointer auf das 'W' und beim dritten Aufruf kommt dann 
NULL zurück.

strtok darf niemals auf String-Literale angewendet werden, weil ja der 
Originalstring manipuliert wird.

von P. S. (Gast)


Lesenswert?

Nick Müller schrieb:
>> Jeder Pointer zeigt dann auf den Anfang eines Strings.
> Ohne es ausprobiert zu haben, aber ich glaub das geht nicht.

Ich verstehe ehrlich gesagt nicht, warum man sein Nichtwissen auch noch 
postet, wenn man sich dessen eigentlich schon bewusst ist.

Wenn wir schon dabei sind: strtok wuerde ich mir gar nicht erst 
angewoehnen, sondern gleich seinen reentranten Vetter strtok_r. Auch in 
nicht parallelen Applikationen kann man mit nicht-reentranten Funktionen 
sehr schnell auf die Nase fallen.

von Michl (Gast)


Lesenswert?

Mir gehts darum ,dass mit der
1
 char* ptr_Datafield[20]
-Methode die einzelnen Strings irgendwo im Speicher des µC stehn und 
evtl. andere Daten überschrieben werden könnten. Oder legt mir strtok 
bei jedem Aufruf reservierte Speicherbereiche an, die NACH(!) der 
while-Schleife noch existent sind? Weil erst da werden die einzelnen 
Tokens weiterverarbeitet (hier zu Testzwecken eine UART-Ausgabe).

von P. S. (Gast)


Lesenswert?

strtok gibt dir Nichts neu allokiertes zurueck, sondern lediglich einen 
Pointer auf eine Position in dem String, den du uebergeben hast. 
Ausserdem sucht es das Ende des Teilstuecks (anhand der uebergebenen 
Tokentrenner) und setzt dort eine 0 als Terminator - es veraendert also 
den String, den du uebergeben hast. Und zuguter Letzt merkt es sich, wo 
es beim naechsten Aufruf weitersuchen muss.

von Nick M. (Gast)


Lesenswert?

> Bitte, lest die Doku zu strtok.

Hab ich doch. Dann ist das Buch doch nicht so toll :-) Denn das steht 
da nicht drinnen. Zumindest nicht explizit.
Im "The Standard C Library" v. P.J. Plauger wirds dann klarer...


> strtok manipuliert den Originalstring, indem es an den Trennungsstellen
> (die durch das Delimiter Token gegeben sind) '\0' einfügt!

War mir nicht bewusst, ist mir nie aufgefallen.


Gruß,
Nick

von Nick M. (Gast)


Lesenswert?

> Ich verstehe ehrlich gesagt nicht, warum man sein Nichtwissen auch noch
> postet, wenn man sich dessen eigentlich schon bewusst ist.

Reichlich bekloppte Aussage! Damit dürften nach deiner Vorschrift keine 
Fragen gestellt werden.


Dennoch Gruß,
Nick

von P. S. (Gast)


Lesenswert?

Nick Müller schrieb:
>> Ich verstehe ehrlich gesagt nicht, warum man sein Nichtwissen auch noch
>> postet, wenn man sich dessen eigentlich schon bewusst ist.
> Reichlich bekloppte Aussage! Damit dürften nach deiner Vorschrift keine
> Fragen gestellt werden.

Geht's noch?!? Du hast Nichts gefragt, du hast Unsinn verbreitet.

Ich bin mit Karl-Heinz oft nicht einer Meinung, aber an seinem 
Fachwissen gibt es wenig zu bezweifeln, das hat er oft genug bewiesen. 
Und du stellst dich da hin und erklaerst, dass du zwar keine Ahnung 
hast, aber seine Ausfuehrungen einfach mal nicht glaubst. Das finde ich, 
mit Verlaub gesagt, ziemlich frech.

von Nick M. (Gast)


Lesenswert?

> Geht's noch?!? Du hast Nichts gefragt, du hast Unsinn verbreitet.

Auf der Boxerzeitung geschlafen?
Oder bist du MaWin?


Explizit grußlos,
Nick

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.