www.mikrocontroller.net

Forum: PC-Programmierung String splitten (Programmiersprache: C)


Autor: Leon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo beisamen,

kann mir jemand dabei helfen einen String nach einem bestimmten 
Trennzeichen zu splitten ? Der String sind immer so aus:  a:b;eineZahl#

Der String wird vom PC zum µC mit UART gesendet. Ich habe es mit 
strtok_r versucht, aber irgendwie klappt das nicht.

Danke !

: Verschoben durch Admin
Autor: Leon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry ! Das Trennzeichen ist das Semikolon ;

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

Bewertung
0 lesenswert
nicht lesenswert
Leon schrieb:

> Der String wird vom PC zum µC mit UART gesendet. Ich habe es mit
> strtok_r versucht, aber irgendwie klappt das nicht.

Dann solltest du deinen Versuch, der "irgendwie nicht klappt" zeigen. 
Dann kann man auch gezielt helfen und auf deine Defizite eingehen.

Autor: Leon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier ist mein Versuch:
char string[] = "a:b;100#"
char *tmp;

char *links;
char *rechts;

links=strtok_r(string,";",&tmp);
rechts=strtok_r(NULL,",",&tmp);

Laut diesen Code müsste doch in "links" a:b und in "rechts" 100# drin 
sein oder nicht ?

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

Bewertung
0 lesenswert
nicht lesenswert
Leon schrieb:
> Hier ist mein Versuch:
>
>
> char string[] = "a:b;100#"
> char *tmp;
> 
> char *links;
> char *rechts;
> 
> links=strtok_r(string,";",&tmp);
> rechts=strtok_r(NULL,",",&tmp);
> 
>
> Laut diesen Code müsste doch in "links" a:b und in "rechts" 100# drin
> sein oder nicht ?

Kommt drauf an, was du unter 'drinn sein' verstehst.
links und rechts sind einfach nur Zeiger in den originalen String, so 
ähnlich wie ein Lesezeichen eine Stelle in einem Buch markiert aber 
selber keinen Text hält.

Aber um auf die Frage einzugehen: Ja, links und rechts müssten auf die 
entsprechenden Abschnitte in string zeigen (und string ist dabei mit \0 
Zeichen modifiziert worden)

Die spannende Frage lautet daher: Was machst du in den nicht gezeigten 
Code-Teilen?

Autor: Leon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
kann ich dann anhand dieser Leesezeichen den Stringabschnitt in eine 
Variable packen, die ich dann z.b. mit UART wieder zurück zum PC sende ?

Wenn ja wie mache ich das am besten ? mit strncpy ?

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

Bewertung
0 lesenswert
nicht lesenswert
Leon schrieb:
> kann ich dann anhand dieser Leesezeichen den Stringabschnitt in eine
> Variable packen, die ich dann z.b. mit UART wieder zurück zum PC sende ?

Sicher kannst du.

> Wenn ja wie mache ich das am besten ? mit strncpy ?

"Am besten" ist immer relativ.

Ja, strncpy wäre eine Möglichkeit. Eine andere wäre es, die einzelnen 
Stringteile gar nicht umzukopieren. Hängt halt alles immer davon ab, ob 
es eine Möglichkeit gibt, dass sich 'string' während dieser Operation 
verändert oder nicht.

Beim Umkopieren wiederrum hat man das Problem, dass man die maximale 
mögliche Länge der Teilstrings vorher abschätzen muss. Wenn es dafür 
eine sinnvolle Obergrenze gibt und ev. Vorkehrungen trifft, dass die 
Teilstrings die Arrays für die Kopie nicht überlaufen, dann ist auch das 
natürlich eine Möglichkeit.
  char string[] = "a:b;100#"
  char *tmp;
  char part1[30];
  char part2[30]; 

  char *links;
  char *rechts;
 
  links=strtok_r(string,";",&tmp);
  rechts=strtok_r(NULL,",",&tmp);

  // die Einzelteile in Sicherheit bringen
  strncpy( part1, links, sizeof( part1 ) );
  part1[ sizeof( part1 ) - 1 ] = '\0';

  strncpy( part2, rechts, sizeof( part2 ) );
  part2[ sizeof( part2 ) - 1 ] = '\0';
  
  // mach was damit
  uart_puts( part1 );
  uart_puts( part2 );

Autor: Bernhard R. (barnyhh)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit strcpy()

Bernhard

BTW: RTFM! (präzise: die Dokumentation der C-Lib)

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein Beispiel oben funktioniert im Prinzip; abgesehen davon,
daß ein Semikolon fehlt.
(Wäre schön, wenn man hier kompilierbaren Originalquelltext
reinkopieren würde, und nicht irgendetwas, was so ähnlich
aussieht).

Hier mit kopieren:
  char string[] = "a:b;100#";
  char *tmp;

  char *links;
  char *rechts;

  char links_kopie[20];
  char rechts_kopie[20];

  links=strtok_r(string,";",&tmp);
  rechts=strtok_r(NULL,",",&tmp);

  printf( "<%s><%s>\n", links, rechts );

  strncpy( links_kopie, links, sizeof(links_kopie) );
  strncpy( rechts_kopie, rechts, sizeof(rechts_kopie) );

  printf( "<%s><%s>\n", links_kopie, rechts_kopie );

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
oh, KHB war wieder schneller

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

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:

>   strncpy( links_kopie, links, sizeof(links_kopie) );
>   strncpy( rechts_kopie, rechts, sizeof(rechts_kopie) );

Aufpassen.
Wenn du strncpy so wie hier benutzt, bist du nicht wirklich sicher. 
strncpy hängt kein \0 Byte an, wenn die Destination droht überzulaufen. 
Du hast dann zwar beim Umkopieren keinen Schaden angestellt, kriegst 
dann aber mit Sicherheit bei der Arbeit mit der Kopie Probleme.

Ist mir ehrlich gesagt ziemlich unverständlich, warum man hier nicht 
konsequent genug war, in diesem Fall ein abschliessendes \0 Byte zu 
fordern. Denn so wie es jetzt ist, ist strncpy auch nur ein 'pain in the 
ass'

Möchte nicht wissen, wieviele Funktionen
char* strncpy_safe( char* dest, const char* source, size_t num )
{
  char* res = strncpy( dest, source, num );
  dest[ num - 1 ] = '\0';
  return res;
}
weltweit mittlerweile geschrieben wurden.

Autor: Bernhard R. (barnyhh)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So ist das strncpy gefährlich. links_kopie und rechts_kopie sind nicht 
zwangsläufig 0-terminiert.

Korrekt lautet die strncpy-mimik hier:

strncpy( links_kopie, links, sizeof(links_kopie) - 1 );
links_kopie(sizeof(links_kopie) - 1) = 0;

... gleichartig mit rechts_kopie.

Bernhard

p.s. da war ich etwas langsam mit dem Schreiben.

Autor: Leon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also hänge ich eine \0 an das Ende des jeweiligen Parts um eine 
Eindeutige Beendigung der Zeichenkette für den PC zu gewährleisten.

Autor: Leon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
und mit   strncpy( part1, links, sizeof( part1 ) ); kopiere ich von 
links in part1 soviel rein, wie in part1 überhaupt rein passt, also 
sizeof( part 1) um somit ein Überlauf zu vermeiden... das klingt sehr 
gut.

Autor: Leon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn ich mit nun mit printf(part1); den Part ausgeben möchte, meldet der 
Compiler eine Warnung: format not a string literal and no format 
arguments.

Ok die letzte Fehlermeldung bedeutet, dass ich keine 
Formatierungsargumente angegeben habe, aber wie löse ich das Problem mit 
dem string ? Oder sollte man lieber nicht printf benutzen um 
Variableninhalte auszugeben ?

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja, das mit der 0 weiß ich.
In so einer Form macht es aber ohnehin nur Sinn, wenn man
sicher ist, daß der Platz reicht. Sonst merkt man den Fehler
ja nicht und arbeitet einfach mit halben Strings weiter.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Leon schrieb:
> Wenn ich mit nun mit printf(part1);...

Das ist Quark.
Wenn man keine Formatierung braucht, ist printf() unpassend.
Und wenn schon, dann printf("%s",part1), weil du sonst Probleme
bekommst, wenn im String ein Prozentzeichen auftaucht.

Autor: Leon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Entschuldigt mich, wenn ich so viel rum antworte, aber wenn ich nun von 
dem part1 nur den zweiten "char" ausgeben möchte, mache ich das mit 
puts(&part1[2]);  Wenn ich nun das ohne & mache bekomm ich ein Fehler, 
da der Compiler ja nicht den char-Wert ausgibt, sondern die 
Speicheradresse vom Zeiger oder wie verstehe ich das ?  Ich weiß das man 
mit dem &-Operator an den Wert der Variablen ran kommt.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lies doch mal ein C-Buch...

Mit puts gibt man nicht ein Zeichen aus, sondern einen
nullterminierten String.
Willst du das zweite Zeichen haben, geht das mit putchar(&part1[1])
([1] statt [2], weil es bei 0 anfängt zu zählen).

puts(&part1[2]) würde ab dem dritten Zeichen bis zum Ende ausgeben.

Autor: Leon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab ein C-Buch hier neben mir legen und lese auch parallel dazu !

Danke ! Ich werde auch keine weiteren Fragen mehr stellen und euch von 
diesen Qualen erlösen g

Autor: JLes (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Programmcode hängt davon ab, was Du mit dem String machen willst, ob 
Du den String vom µC weiter brauchst, und wo Du das Ergebnis halten 
willst.

strtok_r zu benutzen... Na ja, mit einem Mikroskop kann man auch nageln. 
strchr wäre besser.

Angenommen, der String vom µC liegt in instr[]:

char instr[128];

dann, um das erste ';'-Zeichen im instr zu finden, ruft man strchr so 
auf:

char *semi;
. . .

semi = strchr(instr, ';');


Nicht vergessen zu prüfen, ob ';' gefunden wurde:

if (semi==NULL) {
  printf("Scheisse, der µC hat ungültigen String ohne ; geschickt!\n");
  return;
}

Fall 1:

Der Inputstring brauchst Du nicht mehr, dann kannst Du zwei Teile in 
instr halten:

*semi++ = '\0';

Der erste Teil ist instr, der zweite Teil ist semi:

// Testausdruck
printf("Teil 1: %s\nTeil 2: %s\n", instr, semi);

Fall 2:

Der Inputstring brauchst Du weiter, und zwar unverändert, dann musst Du 
die Teile irgendwo hinkopieren.

Fall 2a:

Die Zielstrings (die Teile vom µC-String) sind statische Arrays:

char teil1[128], teil2[128]; // hier wird genug Platz allokiert,
// daher muss man sich nicht um die Länge kümmern

*semi = '\0';
strcpy(teil1, instr);
strcpy(teil2, semi+1);
*semi = ';';   // den µC-String wiederherstellen
// Testausdruck
printf("Teil 1: %s\nTeil 2: %s\n", teil1, teil2);


Fall 2a:

Die Zielstrings werden dynamisch allokiert. Vorteil: der Speicher wird 
sparsamer benutzt; Nachteil: man muss am Ende die Zielstrings freigeben.

char *teil1, *teil2;

*semi = '\0';
teil1 = strdup(instr);
teil2 = strdup(semi+1);
*semi = ';';   // den µC-String wiederherstellen
// Testausdruck
printf("Teil 1: %s\nTeil 2: %s\n", teil1, teil2);


// nachdem die Teile bearbeitet wurden, sollen sie freigegeben werden
free(teil1);
free(teil2);


Wenn Du den Originalstring brauchst, kannst Du auch diesen kopieren und 
weiter mit der Kopie wie im Fall 1 arbeiten:

char instr[128], kopiestr[128];
char *semi;
. . .

strcpy(kopiestr, instr);
semi = strchr(kopiestr, ';');
if (semi!=NULL) {
  *semi++ = '\0';
  // Testausdruck
  printf("Teil 1: %s\nTeil 2: %s\n", kopiestr, semi);
}
else {
  printf("bad string!\n");
}

Autor: Herman Uh. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich lese über die Serielle Konsole Werte ein.

Diese bestehen aus einem Buchstaben(klein) sowie maximal 3 Zahlen.

Wie kann ich nun am besten diese Aufteilung machen.

In cmd lade ich den ersten Buchstaben aus dem string rein. Macht dieser 
keinen Sinn wird der String gelöscht -> Falsche Eingabe.

Wie kann ich nun am einfachsten die restlichen Zeichen aus dem String 
auslesen?
void cmd_switch(void)
{
  char cmd = uart_string[0];
  
  switch(cmd)
  {
    case 'm':
    uart_puts_p(PSTR("MOTOR\r\n"));
    break;
    
    case 'l':
    uart_puts_p(PSTR("LEDS\r\n"));
    break;
    
    case 's':
    uart_puts_p(PSTR("SPEED\r\n"));
    break;
    
    case 'u':
    uart_puts_p(PSTR("UEBERSICHT"));
    break;
    
    default:
    uart_puts_p(PSTR("ERROR\r\n"));
    memset(uart_string, 0, UART_MAXSTRLEN);
  }
}

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Herman Uh. schrieb:
> Diese bestehen aus einem Buchstaben(klein) sowie maximal 3 Zahlen.

Meinst Du so etwas "m123"? Oder so etwas "m 123 456 789"?

Autor: Joachim B. (jar)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl Heinz schrieb:
> Ja, strncpy wäre eine Möglichkeit. Eine andere wäre es, die einzelnen
> Stringteile gar nicht umzukopieren. Hängt halt alles immer davon ab, ob
> es eine Möglichkeit gibt, dass sich 'string' während dieser Operation
> verändert oder nicht.
>
> Beim Umkopieren wiederrum hat man das Problem, dass man die maximale
> mögliche Länge der Teilstrings vorher abschätzen muss.

oder er merkt sich die Position ; +1 und setzt in ; die /0

damit ist der String links terminiert und rechts war er ja schon 
terminiert nur muss er den String rechts als Pointer gemerkt den Start ; 
+1 zuweisen.

Vorteil, kein zusätzlicher Speicherplatzbedarf und kein Umkopieren.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Herman" hat mit seiner nicht wirklich zum Thema passenden Frage eine 
vier Jahre alte Threadleiche ausgebuddelt ... das ist mir leider zu spät 
aufgefallen, und wir Moderatoren können nicht einzelne Beiträge aus 
einem Thread in einen anderen (neuen) verschieben.

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.