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
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
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.
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
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.
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 ;-)
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.