www.mikrocontroller.net

Forum: Compiler & IDEs Problem mit strtok_r()


Autor: Siggi O. (Firma: Uni R.) (pcman)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Siggi O. (Firma: Uni R.) (pcman)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Arc Net (arc)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Siggi O. (Firma: Uni R.) (pcman)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char uint8_t;

struct Cmd_t
{
  int   crc;
  int   Par;
  char  Cmd[20];
  char  Par_1[20];
};

struct RS232_t
{
  char Cmd[200];
};

int main()
{
  struct RS232_t RS232;
  struct Cmd_t Command;

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

  strcpy( RS232.Cmd, "<49;5;XYZ;hello>" );


  // den String hinten um ein Zeichen kürzen
  RS232.Cmd[ strlen( RS232.Cmd ) - 1]  = '\0';

  // und den Startpointer um 1 Zeichen vorsetzen
  first = RS232.Cmd + 1;

  token = strtok_r( first, ";", &tmp );
  Command.crc = atoi( token ); 

  token = strtok_r( NULL, ";", &tmp ); 
  Command.Par = atoi( token );

  token = strtok_r( NULL, ";", &tmp );
  strcpy( Command.Cmd, token );

  token = strtok_r( NULL, ";", &tmp );
  strcpy( Command.Par_1, token );

  printf( "Crc %d\n", Command.crc );
  printf( "Par %d\n", Command.Par );
  printf( "Cmd %s\n", Command.Cmd );
  printf( "1:  %s\n", Command.Par_1 );
}

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.

Autor: Arc Net (arc)
Datum:

Bewertung
0 lesenswert
nicht 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
while (token != NULL) {
    token = strtok_r(NULL, ";", &tmp);
    if (j == 0) {
        Command.Par_l = atoi(token);
    }
    ...
}
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 ;-)

Autor: Siggi O. (Firma: Uni R.) (pcman)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Siggi O. (Firma: Uni R.) (pcman)
Datum:

Bewertung
0 lesenswert
nicht 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 =)

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.