strtok, strtol ersetzen durch sparsamere Varianten

Gast #4422031
Lesenswert?
• ▲
▼
Hallo,

mal eine kurze Frage in die Runde. Gibt es irgendwo eine Code-Sammlung, 
wo man alternative Funktionen findet, die deutlich weniger Flash/RAM 
brauchen?

Ich habe zum Beispiel hier das Problem, dass diese doch recht kurze 
Funktion einen riesigen Speicherzuwachs verursacht.
1
  const char delimiter[] = ",;:";      // Trennzeichen
2
  const unsigned char SIZE_OF_DATA = 5;  // Maximal zu empfangende Datenfelder
3
  unsigned char data[SIZE_OF_DATA];    // Speicher Datenfelder
4
  unsigned char ct = 0;
5

6
  char* ptr = (char*)strtok((char*)USART0_string, delimiter);
7
  while(ptr != NULL && ct < SIZE_OF_DATA)
8
  {
9
    data[ct++] = strtol(ptr, NULL, 16);
10
    ptr = strtok(NULL, delimiter);
11
  }

Ich empfange auf einem Attiny einen String, der immer wie folgt aussieht

char buffer[] = "01:02:0f:0F:FF\n";

Nun habe ich das in meiner Anweisung bislang so gemacht,
das ich jedes Mal eine Zeichenkette bis zum Auftreten des Trennzeichens 
einlese. Die Zeichenkette (in meinem Fall im 2 Zeichen lang + '\0') wird 
dann mit strtol in eine Zahl umgewandelt zur Basis 16. Anschließend 
landet diese in einem Zahlenarray. Sollten einmal, wieso auch immer mehr 
Datenfelder eingelesen werden, so werden die weiteren ignoriert.

Kennt jemand dazu eine smarte Alternative?
Gast #4422068
Lesenswert?
• ▲
▼
Benutze endptr von strtol
1
  char *endptr;
2
  char* ptr = (char*)USART0_string;
3
  do
4
  {
5
    data[ct++] = strtol(ptr, &endptr, 16);
6
    ptr = endptr+1;
7
  }
8
  while(*endptr == ':' && ct <= SIZE_OF_DATA)
oder so (weil ungetestet)
Geht aber nur, wenn der : direkt hinter der Zahl steht.
Gast #4422901
Lesenswert?
• ▲
▼
Hallo Dirk,

danke schon einmal für den Tipp. So funktionierte es nicht direkt. Gab 
aber keine Compilerwarnung, werde es gleich noch einmal weiter testen. 
Es würde auf jedenfall schon mal ein kleines bisschen am Speicher 
sparen.

Würde aber gerne komplett auf string.h und stdlib.h verzichten können. 
Das ist die einzige Stelle im ganzen Programm, wo ich das benötige.
Moderator Persönliche Seite #4422930
Lesenswert?
• ▲
▼
Ich habe aus dem obigen Code-Schnipsel mal ein Programm gemacht, welches 
einfach auf einem PC (Linux, Windows) übersetzbar ist:
1
#include <stdio.h>
2
#include <string.h>
3
#include <stdlib.h>
4

5
char buffer[] = "01:02:0f:0F:FF\n";
6

7
int main ()
8
{
9
    const char delimiter[] = ",;:";      // Trennzeichen
10
    const unsigned char SIZE_OF_DATA = 5;  // Maximal zu empfangende Datenfelder
11
    unsigned char data[SIZE_OF_DATA];    // Speicher Datenfelder
12
    unsigned char ct = 0;
13

14
    char* ptr = buffer;
15

16
    while(ptr != NULL && ct < SIZE_OF_DATA)
17
    {
18
        data[ct++] = strtol(ptr, NULL, 16);
19
        ptr = strtok(NULL, delimiter);
20
    }
21

22
    printf ("%s%02x:%02x:%02x:%02x:%02x\n", buffer, data[0], data[1], data[2], data[3], data[4]);
23

24
    return 0;
25
}

Output:

01:02:0f:0F:FF
01:01:00:00:00

Manchmal ändert sich die 2. Zeile auch.

Fazit: Das Programm tut überhaupt nicht, was es soll. Wurde das 
überhaupt mal getestet?

Alternative ohne die Kanonen auf Spatzen (strtok & strtol) folgt 
gleich...
Moderator Persönliche Seite #4422937
Lesenswert?
• ▲
▼
Und hier die platzsparende und schnellere Variante, die ohne strtok() 
und strtol() auskommt:
1
#include <stdio.h>
2
#include <stdint.h>
3

4
/*---------------------------------------------------------------------------------------------------------------------------------------------------
5
 * htoi: convert n hex digits (max is 4) of a string into decimal value
6
 *---------------------------------------------------------------------------------------------------------------------------------------------------
7
 */
8
uint16_t
9
htoi (char * buf, uint8_t max_digits)
10
{
11
    uint8_t     i;
12
    uint8_t     x;
13
    uint16_t    sum = 0;
14

15
    for (i = 0; i < max_digits && *buf; i++)
16
    {
17
        x = buf[i];
18

19
        if (x >= '0' && x <= '9')
20
        {
21
            x -= '0';
22
        }
23
        else if (x >= 'A' && x <= 'F')
24
        {
25
            x -= 'A' - 10;
26
        }
27
        else if (x >= 'a' && x <= 'f')
28
        {
29
            x -= 'a' - 10;
30
        }
31
        else
32
        {
33
            x = 0;
34
        }
35
        sum <<= 4;
36
        sum += x;
37
    }
38

39
    return (sum);
40
}
41

42

43
char buffer[] = "01:02:0f:0F:FF\n";
44

45
int main ()
46
{
47
    const unsigned char SIZE_OF_DATA = 5;  // Maximal zu empfangende Datenfelder
48
    unsigned char data[SIZE_OF_DATA];    // Speicher Datenfelder
49
    unsigned char ct = 0;
50

51
    char* ptr = buffer;
52

53
    while (*ptr)
54
    {
55
        data[ct++] = htoi (ptr, 2);
56
        ptr += 3;
57
    }
58

59
    printf ("%s%02x:%02x:%02x:%02x:%02x\n", buffer, data[0], data[1], data[2], data[3], data[4]);
60

61
    return 0;
62
}

Output:
01:02:0f:0F:FF
01:02:0f:0f:ff

Passt also.

Voraussetzung ist aber, dass der TO das Format von Buffer 
("xx:xx:xx:xx:xx\n") vorher prüft. Sonst geht das hier in die Hose.
#4423104
Lesenswert?
• ▲
▼
Hallo Frank,

klasse Ansatz. Hab mir deine Idee auch erst einmal direkt abgespeichert. 
Habe es bei mir ähnlich gelöst mit string und stdlib, da es mir so im 
Buch auch nicht anders erklärt wurde. Leider findet meisten keine 
Alternativen und muss die selber schreiben.

Ich werde deine Idee mal versuchen aufzuarbeiten. Habe auch immer einen 
String, den ich splitten muss. Bekomme aber ab und zu ein paar falsche 
Werte rein. Mal schaun ob ich das evtl. etwas passend umschreiben kann 
;)

Werd dann meinen Ansatz hier auch noch mit reinstellen.
#4423314
Lesenswert?
• ▲
▼
Hab das ganze mal etwas umgeschrieben, etwas statisch, aber sollte nicht 
das Problem sein.

Mit
1
// CMD_ERROR == 0x00
2
uint8_t checkCommand(char *str)
3
{
4
  const uint8_t MAX_STRLEN = 11;
5
  uint8_t ct = 0;
6
  
7
  while(*str != '\0')
8
  {
9
    if(ct%3 != 2)
10
    {
11
      if(!
12
      ((*str >= 'a' && *str <= 'f') ||
13
      (*str >= 'A' && *str <= 'F') ||
14
      (*str >= '0' && *str <= '9'))
15

16
      ) return CMD_ERROR;
17
    }
18
    else
19
    {
20
      if(*str != ':') return CMD_ERROR;
21
    }
22
    ct++;
23
    *str++;
24
  }
25
  if(ct != MAX_STRLEN) return CMD_ERROR;
26
  else return 1;
27
}
wird überprüft, ob der String = "xx:xx:xx:xx" entspricht.

Wenn diese Funktion 1 zurückgibt, kann splitCommand genutzt werden.
1
void splitCommand(char *str, uint8_t *data, uint8_t SIZE_DATA)
2
{
3
  uint8_t ct = 0;
4
  char* ptr = str;
5
  while(*ptr && ct < SIZE_DATA-1)
6
  {
7
    data[ct++] = htoi(ptr, 2);
8
    ptr += 3;
9
  }
10
}
Scheint soweit zu gehen.

Antwort schreiben

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.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.