Forum: Mikrocontroller und Digitale Elektronik Effiziente string replace Funktion


von Artur (Gast)


Lesenswert?

Ich habe habe mehrere Webseiten, die im stm32 Flash liegen. Die HTML 
Daten enthalten Variablen, die ich vor der Ausgabe noch ersetzen muss. 
Dazu verwende ich folgende Funktion:
1
char str_replace(char *string_in, char *search, char *replace) {
2
3
  /**/
4
  char *tempString, *tok;
5
  int len = 0;
6
7
  tok = strstr(string_in, search);
8
  if(tok == NULL) {
9
    return 0;
10
  }
11
  //printf("\r\ncO: %d\r\ncS: %d\r\ncR: %d", strlen(string_in),  strlen(search), strlen(replace) );
12
  
13
  tempString = (char*)malloc( ( strlen(string_in) - strlen(search) + strlen(replace) ) * sizeof(char));
14
  if(tempString == NULL) {
15
    return NULL;
16
  }
17
  strcpy(tempString, string_in);
18
  len = (tok - string_in);
19
  string_in[len] = '\0';
20
  strcat(string_in, replace);
21
  len = len + strlen(search);
22
  strcat(string_in, (char*)tempString + len);
23
  free(tempString);
24
  
25
  return 1;
26
}

Jedoch bleibt der STM32 immer wieder hängen und zwar oft reproduzierbar.
Ich vermute, dass es was mit der Speicherreservierung zu tun hat. Meine 
Funktion frisst auch ordentlich Platz im RAM. Gibt es eine bessere 
Methode, bei der man mit weniger Speicher auskommt? Das Problem ist, 
dass die Variablennamen und die dazugehörigen Werte nicht die gleiche 
Länge haben, sonst wäre es kein Problem.


Beispiel:
sprintf(cTempArr, "%2X-%2X-%2X-%2X-%2X-%2X",MAC_ADDR0, MAC_ADDR1, 
MAC_ADDR2, MAC_ADDR3, MAC_ADDR4, MAC_ADDR5);
str_replace(dhtml, "$LAN:MAC$", cTempArr);


MfG

von Peter II (Gast)


Lesenswert?

Artur schrieb:
> Gibt es eine bessere
> Methode, bei der man mit weniger Speicher auskommt?

warum machst du die ersetzung einzeln? Man könnte die Daten auch während 
der ausgabe ersetzen, dann brauchst du überhaupt keinen RAM dafür.

von Klaus W. (mfgkw)


Lesenswert?

Vielleicht da einsetzen, wo es auch ausgegeben wird, anstatt erst in 
einem langen String zu sammeln?

von Karl H. (kbuchegg)


Lesenswert?

Grundsätzlich kommst du natürlich am Problem, dass du mehr Speicher 
allokieren musst nicht herum, solange die die generelle Strategie nicht 
veränderst (zb Ersetzen erst während der Ausgabe)

Allerdings kannst du dir ja den temporär angelegten Speicher (samt 
zugehöriger Länge) auch für den nächsten Aufruf aufheben und wenn die 
Funktion dann drauf kommt, dass dieser Speicher ausreichen würde, musst 
du keinen neuen Speicher anlegen sondern kannst den vorhandenen 
recyclen.

Der Speicher Fragmentierung würde das sicherlich gut tun.

von Artur (Gast)


Lesenswert?

LwIP ließt die Daten sonst direkt aus dem Flash, da wüsste ich nicht, 
wie ich die sonst ersetzen könnte. Mein Problem ist, dass der STM 
Hardfault handler hat, komischerweise bei einem HTML File, welches nicht 
größer als manch andere Files sind. Außerdem habe ich statische Sachen, 
wie Header und Footer der Webseite, ich lese deshalb alle Daten erst in 
RAM ein, ersetze die dynamischen Daten im HTMl file und baue vor der 
Ausgabe alles mit strcat zusammen (wobei ich da auch Speicher malloc 
habe).

html_file = malloc(file_header.len + strlen(dhtml) + file_footer.len);

Alles scheitern nur an der string replace Funktion, ich habe das Gefüll, 
dass es nicht an der malloc Funktion liegt, sondern an der Umsetzung der 
string replace Funktion, wobei mir kein besserer Weg einfällt.

von Karl H. (kbuchegg)


Lesenswert?

Artur schrieb:

> Alles scheitern nur an der string replace Funktion, ich habe das Gefüll,
> dass es nicht an der malloc Funktion liegt, sondern an der Umsetzung der
> string replace Funktion, wobei mir kein besserer Weg einfällt.


Ich habe eher das Gefühl, dein eigentliches Problem liegt ganz woanders 
im Code. Was du siehst sind lediglich die Auswirkungen, aber nicht die 
Ursache. Ein Programmfehler kann ganz woanders im Code stecken und das 
System soweit 'destabilisieren', dass der Fehler sich dadurch bemerkbar 
macht, dass diese Funktion nicht mehr richtig funktioniert.

von Peter D. (peda)


Lesenswert?

Wenn ich Deine Funktion richtig verstehe, ist *string_in nach dem Aufruf 
größer.
Wenn Du also nur den nötigen Speicher vor dem Aufruf reserviert hast, 
kracht es natürlich.

Im allgemeinen sollte man malloc nur da benutzen, wo es nötig ist. Also 
nur dann wenn die erzeugende Funktion den Speicher nicht wieder 
freigibt, sondern eine andere.
Ansonsten ist es effektiver, wenn man einfach eine lokale Variable der 
maximal benötigten Größe anlegt.


Peter

von Peter D. (peda)


Lesenswert?

Ich hatte mir mal die Funktion strins() geschrieben.
Damit könntest Du Deine Funktion ganz ohne ein zusätzliches Array 
schreiben:
1
/**********************  Einfügen von src in dst ***********************/
2
3
void strins(char *dst, char *src)
4
{
5
  unsigned int i, j;
6
  for ( i = 0; src[i++]; );            /* Länge von Quelle */
7
  for ( j = 0; dst[j++]; );            /* Länge von Ziel   */
8
  for ( i--; j--; dst[i+j] = dst[j] ); /* Ziel verschieben */
9
  for ( ; i--; dst[i] = src[i] );      /* Quelle einfügen  */
10
}

Natürlich muß auch dann *string_in ausreichend groß sein, damit durch 
das Einfügen kein Überlauf erfolgt.

Peter

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger schrieb:
> Wenn ich Deine Funktion richtig verstehe, ist *string_in nach dem Aufruf
> größer.
> Wenn Du also nur den nötigen Speicher vor dem Aufruf reserviert hast,
> kracht es natürlich.

@Artur

Da hat er recht (und ich habs übersehen :-)

Und was wir alle bisher übersehen haben: Deine Allokierung des 
temporären Strings ist um 1 Zeichen zu kurz.

Bei solchen Sachen IMMER die Länge des allokierten Speichers mitgeben, 
damit die Funktion darauf achten kann, diese nicht zu überschreiten.
Unzählige Programmabstürze und Hackerangriffe gehen auf das Konto dieses 
Versäumnisses und haben C den Ruf eines unhandhabbaren Monsters 
eingebracht.
1
char str_replace(char *string_in, size_t string_in_size,
2
                 char *search, char *replace) {
3
4
  char *tempString, *tok;
5
  int len = 0;
6
7
  tok = strstr(string_in, search);
8
  if(tok == NULL) {
9
    return 0;
10
  }
11
12
  size_t newLen = strlen(string_in) - strlen(search) + strlen(replace) + 1;
13
14
  if( newLen > string_in_size )
15
    return 0;
16
17
  tempString = malloc( newLen * sizeof(char) );
18
  if(tempString == NULL)
19
    return 0;
20
21
  // erst jetzt ist sicher gestellt, dass die Operation gut gehen wird
22
23
  ...
24
}

Wenn du aber sowieso den Originalstring veränderst, brauchst du doch 
auch den temporären String nicht:

im Original die Einfügestelle suchen
Den Teil dahinter in der benötigten Größe nach hinten verschieben und so 
eine Lücke bauen.
In die Lücke den Ersatztext einsetzen.

Prüfen, ob das Original über genügend Platz verfügt, musst du aber so 
und so.

von Artur (Gast)


Lesenswert?

>Bei solchen Sachen IMMER die Länge des allokierten Speichers mitgeben,
>damit die Funktion darauf achten kann, diese nicht zu überschreiten.

Reicht es nicht, wenn ich innerhalb der Funktion die Länge abfrage?

>Strings ist um 1 Zeichen zu kurz.
Wo du Recht hast...Thx


>Wenn du aber sowieso den Originalstring veränderst, brauchst du doch
>auch den temporären String nicht.
Werde am Montag probieren, es ohne den Temp-Speicher umzusetzen.

von Karl H. (kbuchegg)


Lesenswert?

Artur schrieb:
>>Bei solchen Sachen IMMER die Länge des allokierten Speichers mitgeben,
>>damit die Funktion darauf achten kann, diese nicht zu überschreiten.
>
> Reicht es nicht, wenn ich innerhalb der Funktion die Länge abfrage?

Innerhalb der Funktion hast du nur einen Pointer auf den Speicher. Damit 
kannst du nicht feststellen wie groß der Speicherbereich ist. Du 
kannst feststellen, wie lang der String ist, aber das sagt ja nichts 
darüber aus, wie groß das Array ist, in dem der String abgelegt wurde. 
Diese Information braucht die Funktion und da sie diese nicht selbst 
ermitteln kann, muss sie der Aufrufer mitgeben.

im Grunde eine Variation von
1
void foo( char *pMem )
2
{
3
  strcpy( pMem, "Hallo World" );
4
}
5
6
int main()
7
{
8
  char str[3];
9
  foo( str );
10
}

"Hallo World" passt niemals in einen char[3] rein. foo hat aber keine 
Chance festzustellen, dass sich hinter pMem ein char[3] verbirgt.


So programmiert, ist das kein Problem mehr.
1
void foo( char *pMem, size_t size )
2
{
3
  if( size > 11 )
4
    strcpy( pMem, "Hallo World" );
5
}
6
7
int main()
8
{
9
  char str[3];
10
  foo( str, sizeof( str ) );
11
}

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.