Forum: Compiler & IDEs Problem mit strtok_r()


von Siggi O. (Firma: Uni R.) (pcman)


Lesenswert?

Hallo Leute,
ich arbeite an einem kleinen Protokoll zur Datenübertragung und möchte 
eingehende Strings in Unterkomponenten zerlegen. Dazu verwende ich den 
Befehl strtok_r(). Leider bekomme ich bei der Zerlegung einen mir nicht 
erklärbaren fehler.

Zum Protokoll:
Es werden Zeichenketten des Stils <49;5;XYZ;hello> übermittelt.
49 ist irgendeine Prüfsumme
5 ist die Länge des Parameters von XYZ (also hier von hello)
XYZ soll irgendein Befehl sein (hat immer die Länge 3)

Bei meiner Funktion decode() wird nun von "<49;5;XYZ;hello>" das erste 
und das letzte Zeichen abgeschnitten, es bleibt also "49;5;XYZ;hello" 
über.

Dann wird das ganze mit strtok_r zerlegt.

Hier etwas Code:

  char *token;
  char *tmp;
  uint8_t j=0;

  //lösche ersten char
  for (int i=0; i<strlen(RS232.Cmd); i++) {
    RS232.Cmd[i]=RS232.Cmd[i+1];
  }
  RS232.Cmd[strlen(RS232.Cmd)-1]='\0'; //lösche letzten char

  token=strtok_r((char*)RS232.Cmd,";", &tmp);
  Command.crc=atoi(token); //ergibt die crc

  while (token != NULL ) {
    if (j==0) {
      token = strtok_r(NULL,";", &tmp);
      uart_puts(token);
      //Command.Par_l=atoi(token);
    }
    else
    if (j==1) {
      token = strtok_r(NULL,";", &tmp);
      for(int i=0; i<3; i++) Command.Cmd[i]=token[i];
    }
    else
    if (j==2) {
      token = strtok_r(NULL,";", &tmp);
      for(int i=0; i<Command.Par_l; i++) Command.Par[i]=token[i];
    }
    j++;

  }

Das Problem taucht auf, sobald ich (bei j==0) den zweiten Substring 
bekommen will, also um genau zu sein, wenn ich die "5" raustrennen will.
Da das nicht funktioniert hat, habe ich mir das token einfach mal 
anzeigen lassen. Dabei ist mir aufgefallen, dass da nie eine 5 steht, 
sondern ein "5(". Wo kommt dieses '(' her?
Wenn ich als Checksumme jetzt keine zweistellige Zahl übermittle (also 
z.B.: "<4;5;XYZ;hello>" dann funktioniert das heraustrennen der 5 
einwandfrei.

Mein System: ATMega23, avr-gcc (GCC) 4.1.2 (WinAVR 20070525)

Habt vielen Dank fpr eure Hilfe, ich bin mit meinen Lateien nämlich am 
Ende ;(

Viele Grüße,
Simon

von Siggi O. (Firma: Uni R.) (pcman)


Lesenswert?

Hallo nochmal.
Ich habe festgestellt, dass wenn ich die Zeile

Command.crc=atoi(token);

herauskommentiere das Problem nicht mehr vorliegt. Kann mir jemand 
erklären, welchen einfluss atoi auf token nimmt?

Viele Grüße,
Simon

von Arc N. (arc)


Lesenswert?

Das sollte funktionieren (zumindest das aufspalten).
Nur ist das in der jetzigen Form eine Endlosschleife (token wird nie 
NULL) und beim Befüllen von Command.Cmd bzw. Command.Par wird das 
terminierende '\0' (absichtlich?) nicht mitkopiert.

von Siggi O. (Firma: Uni R.) (pcman)


Lesenswert?

Hallo,
nein eigentlich ist das nicht beabsichtigt,
aber ich verstehe nicht ganz:
- wieso wird token niemals NULL?
- wie kann ich das terminierende \0 mitkopieren?

Viele Grüße,
Simon

von Karl H. (kbuchegg)


Lesenswert?

Simon O. wrote:

> - wieso wird token niemals NULL?
Sollte eigentlich schon NULL werden

> - wie kann ich das terminierende \0 mitkopieren?

wie wäre es mit dem Einsatz von strcpy() anstatt deiner
selbstgebastelten Schleifen.


Vereinfach mal dein Programm und sieh dir jeden Teilstring an.
Das sieht alles extrem überkompliziert aus.
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <string.h>
4
5
typedef unsigned char uint8_t;
6
7
struct Cmd_t
8
{
9
  int   crc;
10
  int   Par;
11
  char  Cmd[20];
12
  char  Par_1[20];
13
};
14
15
struct RS232_t
16
{
17
  char Cmd[200];
18
};
19
20
int main()
21
{
22
  struct RS232_t RS232;
23
  struct Cmd_t Command;
24
25
  char *first;
26
  char *token;
27
  char *tmp;
28
  uint8_t j=0;
29
30
  strcpy( RS232.Cmd, "<49;5;XYZ;hello>" );
31
32
33
  // den String hinten um ein Zeichen kürzen
34
  RS232.Cmd[ strlen( RS232.Cmd ) - 1]  = '\0';
35
36
  // und den Startpointer um 1 Zeichen vorsetzen
37
  first = RS232.Cmd + 1;
38
39
  token = strtok_r( first, ";", &tmp );
40
  Command.crc = atoi( token ); 
41
42
  token = strtok_r( NULL, ";", &tmp ); 
43
  Command.Par = atoi( token );
44
45
  token = strtok_r( NULL, ";", &tmp );
46
  strcpy( Command.Cmd, token );
47
48
  token = strtok_r( NULL, ";", &tmp );
49
  strcpy( Command.Par_1, token );
50
51
  printf( "Crc %d\n", Command.crc );
52
  printf( "Par %d\n", Command.Par );
53
  printf( "Cmd %s\n", Command.Cmd );
54
  printf( "1:  %s\n", Command.Par_1 );
55
}

Funktioniert so, wie man es erwartet. Dein Fehler liegt wahrscheinlich
in dem Code den du nicht zeigst. Ich vermute mal das übliche: Irgend-
ein Array Overflow.

von Arc N. (arc)


Lesenswert?

Wieso wird token niemals NULL?
Was passiert wenn j == 2 ist?
token zeigt auf "hello" und wird kopiert, j wird 3, danach wird wieder 
token != NULL ausgewertet, j wird 4... d.h. z.B. soetwas daraus machen
1
while (token != NULL) {
2
    token = strtok_r(NULL, ";", &tmp);
3
    if (j == 0) {
4
        Command.Par_l = atoi(token);
5
    }
6
    ...
7
}
oder direkt eine for-Schleife draus machen oder die Lösung von oben 
nehmen...

Wie kann ich das terminierende \0 mitkopieren?
Ein Zeichen mehr umkopieren.

> Ich vermute mal das übliche: Irgendein Array Overflow.
aber strcpy vorschlagen ;-)

von Siggi O. (Firma: Uni R.) (pcman)


Lesenswert?

hm okay, wenn das überkompliziert ist wundert's mich nicht, dass ich 
hier ewigkeiten über den Protokoll brüte.
Vielen Dank jedenfalls, werde mal den Code vereinfachen. Übrigens: den 
Code, den ich nicht gezeigt habe sieht ziemlich genau so aus wie du ihn 
hingeschrieben hast.
Viele Grüße Simon

von Siggi O. (Firma: Uni R.) (pcman)


Lesenswert?

Hallo,
mit der Vereinfachung hat alles wesentlich passender hingehauen. Ich 
habe auch noch ein Array vergrößert (Command.Cmd), das hatte nämlich 
regelmäßig einen Overflow gehabt deswegen kam häufig sch*** raus ;\

Aber dank euch schlauen Köpfen bin ich jetzt ein Stück weiter.
Viele Grüße,
Simon

P.S.: Bin auch noch AVR-GCC anfänger, ursprünglich komme ich aus der VCL 
verwöhnten Welt von Borland ;) Insofern gibt's noch viel zu lernen =)

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.