Forum: Compiler & IDEs strstr?


von Christian (Gast)


Lesenswert?

Hi,

ich hab mal wieder eine Frage:

ich hab eine Funktion, die in einem String einen Platzahlter gegen
einen anderen String ersetzen soll. Aufgrund der Probleme hab ich die
nun mal abgespeckt und suche nur noch nach dem string.

void StringReplace(char* string, const char* find, char* replace)
{
  char* pos;

  pos = strstr(string, find);

  if (pos)
  {
    strcpy(string, pos);
  }
}

Die Platzhalter (parameter find) lege ich als konst. Strings im progmem
an, wie in den FAQs beschrieben:

PGM_P placeholders[] PROGMEM = {placeholder_weight,
placeholder_weight_test};

void GetPlaceHolder(char* buffer, int index)
{
  PGM_P p;
  memcpy_P(&p, &placeholders[index], sizeof(PGM_P));
  strcpy_P(buffer, p);
}

Der Aufruf von StringReplace erfolgt dann so in einer Funktion:

...
char buffer[20];
char buffer1[20];
GetPlaceHolder(&buffer[0], 0);
GetPlaceHolder(&buffer1[0], 1);
StringReplace(&(display.content_text[0]), &buffer[0], &buffer1[0]);
...

De gesuchte String wird aber nicht gefunden, obwohl enthlaten. Ändere
ich die Funktion hingegen so ab:

void StringReplace(char* string, const char* find, char* replace)
{
char buf[5];
  char* pos;
buf[0] = '#';
buf[1] = 'w';
buf[2] = 'm';
buf[3] = '#';
buf[4] = 0;
  pos = strstr(string, &buf[0]);

  if (pos)
  {
    strcpy(string, pos);
  }
}

Wird der Suchstring gefunden. Auch der Aufruf von StringReplace mit
PSTR("#wm#") oder "#wm#" bringt nicht das gewünschte Ergebnis. Was
mache ich hier falsch?

Danke schonmal,
Christian

von Feadi (Gast)


Lesenswert?

Sag doch mal bitte:
- Was übergibst Du deiner Funktion?
- Was kommt dabei raus?
- Was soll rauskommen?

Feadi

von Fritz G. (fritzg)


Lesenswert?

PGM_P p;
  memcpy_P(&p, &placeholders[index], sizeof(PGM_P));


???

Du glaubst dass du ins Flash schreiben kannst? Ne, memcpy_P liest aus
dem Flash und kopiert ins RAM.

von Christian (Gast)


Lesenswert?

Hi,

Wieso schreibe ich ins flash? Das ist das Originalbeispiel aus den
FAQs, das zeigt, wie man aus dem Flash ins Memory schreibt. Wie kommst
Du darauf, das hier irgendwas ins Flash kopiert wird oder werden soll?
Man sieht in dem ersten Beitrag ganz klar, welche strings aus dem Flash
kommen. Das sind genau find und replace. Geschrieben wird ausschließlich
in die Variable string, wobei das Schreiben, wie ich bereits beschrieben
habe, nicht das Problem ist sondern vorher der string search.

Die Funktion StringReplace erklärt sich von selbst. Man übergibt einen
string (null terminiert), sowie zwei weitere strings (find, replace).
In der abgespeckten Version oben soll die funktion in string nach find
suchen (strstr) und wenn gefunden, dann wird hier zur Vereinfachung in
string erstmal einfach der Teilstring kopiert, der gefunden wurde.

Was ich an die Funktion übergebe ist oben ausführlich beschrieben,
ebenso was ich erwarte, das passieren soll. Ebenso ist beschrieben,
dass es mit dem Beispiel aus den FAQs nicht funktioniert, mit dem
setzen der einzelnen chars aber schon.

Die zentrale Frage ist und bleibt (uunabhängig von allen funktionen
oben): Wieso funktioniert strstr nicht, wenn ich dort eine String aus
dem Flashram gemäß der Anleitung aus den FAQs übergebe, wohl aber, wenn
ich diesen String einzeln per characters zusammenbaue?

Ich weiss, dass ich nicht ins flash schreiben kann und beabsichtige
auch nicht, dies zu tun. Ich will lediglich in einem String mit einem
Platzhalter diesen Platzhalter durch einen anderen String ersetzen.
Sowohl Platzhalter als auch ersetzter String sind im Flashram abgelegt.
Vielleicht hat ja jemand ein Beispiel dafür?

Danke,
Christian

von Werner B. (Gast)


Lesenswert?

Codeauschnitt von oben

void GetPlaceHolder(char* buffer, int index)
{
  PGM_P p;
  memcpy_P(&p, &placeholders[index], sizeof(PGM_P));
...

(Katrierter) Prototyp von memcpy:

memcpy(ziel, quelle, länge);

Darum

von Werner B. (Gast)


Lesenswert?

Warum so kompliziert (und zudem falsch)?

mach doch einfach

void GetPlaceHolder(char* buffer, int index)
{
  memcpy_P(buffer,
           placeholders[index],
           strlen_P(placeholders[index]));
...

Wobei das warscheinlich auch nicht so funtionieren wird wie Du Dir das
vorstellst, denn aller Vorraussicht nach wird die Länge des
Platzhalters zu 99,9% immer verschieden von der Länge des Ersatzstrings
sein.

von Fritz G. (fritzg)


Lesenswert?

> Wieso schreibe ich ins flash?

Weil du mit
PGM_P p;
den Zielspeicher ins Flash legst.

von Christian (Gast)


Lesenswert?

Nöö, vollkommen falsch:

Aus der original Doku der AVR GCC lib:

memcpy_P: This is a special version of the memcpy function that copies
data from program memory to RAM.

Da PROGMEM gleichbedeutend mit FLASH (siehe auch GCC Tutorial) bedeutet
dies, die Funktion kopiert vom(!) Flash ins(!) RAM, genau, wie ich das
beabsichtige und nicht etwa wird irgendwas ins Flash kopiert. Der
Mechanismus ist so wie von mir verwendet auf mehreren AVR Seiten
dokumentiert (siehe zB GCC Tutorial). Ziel des Ganzen ist es einfach,
die im Flash definierten Daten in den Hauptspeicher zu kopieren um
diesen dort ganz normal wie alle Daten im RAM zu verwenden.

Davon ab: Wenn ich die vorgeschlagene Variante verwende, muss ich nach
dem Aufruf das Gerät ausschalten, da es gnadenlos abstürzt (passiert
beim Aufruf von memcpy_P)

von Karl H. (kbuchegg)


Lesenswert?

Die Sache mit memcpy_P duerfte darauf beruhen, dass
Wernder nicht durchschaut hat, worauf Du mit GetPlaceHolder
hinaus willst. Hab selbst 3 mal hinschauen mussen um zu
erkennen was da vor sich geht.

> Die zentrale Frage ist und bleibt (uunabhängig von allen
> funktionen oben): Wieso funktioniert strstr nicht, wenn ich
> dort eine String aus dem Flashram gemäß der Anleitung aus den
> FAQs übergebe,

Das tust Du ja gar nicht. Du kopierst ja den String aus
dem Flash zunaechst ins SRAM und arbeitest von dort weiter.
Lass Dir doch den String im SRAM mal ausgeben damit Du siehst
ob der dort nochr ichtig ist, oder ob der schon falsch ist.

> Wird der Suchstring gefunden. Auch der Aufruf von StringReplace
> mit PSTR("#wm#") oder "#wm#" bringt nicht das gewünschte
Ergebnis.

Hmm. Zumindest letzteres sollte funktionieren.

Aendere doch mal die Funktion ob, so dass sie die die Strings
iregendwo ausgibt, damit Du checken kannst, was denn da in
die Funktion heineingeht:

void StringReplace(char* string, const char* find, char* replace)
{
  printf( "string: %s\n", string );
  printf( "find: %s\n", find );

  char* pos;

  pos = strstr(string, find);

  if (pos)
  {
    strcpy(string, pos);
  }
}

Ich weiss jetzt nicht welche Moeglichkeiten zum debuggen
Du hast. Gegenenfalls musst Du halt fuer den printf was
anderes finden. Aber mit einem Aufruf von


int main()
{
  char buffer[20] = "t#wm#";

  StringReplace( buffer, "#wm#", 0 );
}

Sollte da in StringReplace was vernuenftiges ausgegeben werden.

von Christian (Gast)


Lesenswert?

Hallo Karl,

>> Das tust Du ja gar nicht. Du kopierst ja den String aus
>> dem Flash zunaechst ins SRAM und arbeitest von dort weiter.

Du hast natürlich recht. Ich habe mich mißverständlich ausgedrückt.
Natürlich hole ich mir die Strings ins RAM, da strstr ja nicht auf den
FlashRam zugreift. Vermutlich wäre die richtige Lösung ein strstr_P,
aber das gibt es leider nicht.

Die Kontrollausgabe habe ich bereits gemacht, in dem ich die Strings in
"find" und "replace" einfach per strcpy in die variable "string"
kopiert habe (nach dem If). Diese wird nach StringReplace auf einem
Display ausgegeben. Der Witz an der Sache: "Find" wird auch mit
GetPlaceHolder geholt korrekt ausgegeben. Den Mechanismus in
GetPlaceHolder verwende ich an etlichen anderen Stellen bereits und der
funktioniert überall problemlos.

Mein Problem ist, das ich einfach nicht sehe, wo das Problem ist.
Vielleicht habe ich einfach einen Knick im Hirn, aber ich seh's
nicht.

Debuggen kann ich leider gar nicht, da es sich um eine sehr komplexe
Hardware handelt. Aber natürlich kann ich Strings und Zahlen ausgeben
und Keyboardeingaben machen.

Ich probiere mal Deine Vorschläge aus, vielleicht bringt mich das
weiter.

Danke für das konstruktive Feedback! :-)

Christian

von Christian (Gast)


Lesenswert?

Es geht nun! Es lag weder an der Funktion StringReplace noch an
GetPlaceHolder. Beide funktionieren einwandfrei (bereits im ersten
Beitrag war das so).

Das Problem war ein Makro PMSTR, was ich gebaut habe um nicht jedesmal
"const string xyz[] PROGMEM = "string";" schreiben zu müssen. Das
Makro enthielt ein #string für den Stringparameter. Nur hatte ich den
saublöden Fehler gemacht "PMSTR(placeholder1, "#wm#")" zu
schreiben. Da #string aber auch die Quotes mit in den String packt, sah
das Ergebnis so aus "const string xyz[] PROGMEM =
"\"string\"";", es waren also die Quotes mit im String,
grmpf#$%&§...

Unglücklicherweise ist dann noch dazugekommen, das es in meinem Font
aus Platzgründen keine Quotes gibt und somit diese nicht ausgegeben
wurden. Doppelt blöd also....

von Christian (Gast)


Lesenswert?

Und hier noch die Implementierungen, falls jemand Interesse daran hat:

// Deklaration von Strings im FLASH
const string ph_1[] PROGMEM = "test1";
const string ph_2[] PROGMEM = "test2";

// Alle strings in dieses Array gepackt
PGM_P placeholders[] PROGMEM =
{
        ph_1, ph_2
};

// Hier werden die flash strings in einen Buffer im Ram kopiert
void GetPlaceholder(char* buffer, int index)
{
  PGM_P p;
  memcpy_P(&p, &placeholders[index], sizeof(PGM_P));
  strcpy_P(buffer, p);
}

// Und diese Funktion ersetzt den Teilstring "find" in "string"
durch den string in "replace". Ist sicher nicht 100%ig, aber für
meine Zwecke reichts. find und replace können unterschiedliche Längen
haben.
void StringReplace(char* string, char* find, char* replace)
{
  char temp[256];
  char* pos;
  int lfind,lreplace;

  pos = strstr(string, find);

  if (pos)
  {
    lfind = strlen(find);
    lreplace = strlen(replace);
    if (lreplace <= lfind)
    {
      strcpy(pos + lreplace, pos+lfind);
      strncpy(pos, replace, lreplace);
    }
    else
    {
      strcpy(&temp[0], pos);
      strncpy(pos, replace, lreplace);
      strcpy(pos+lreplace, &temp[lfind]);
    }
  }

}

von Werner B. (Gast)


Lesenswert?

Seit AVR-libC v1.4.2 gibt es ein strstr_P.

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.