Eine Zeichenkette in einer Zeichenkette finden

Hallo zusammen,

ich benutze zur Zeit einen Arduino Mega mit einem 20x4 I2C Display und einem 4x4 Tastenpad.
Mein Ziel ist es, dass durch das Tastenpad ein Datum (Geburtsdatum) eingegeben werden soll. Anhand dieses Datums soll ein zugeordneter Name auf dem Display erscheinen. Ich bin jetzt schon einige Tage am Lesen, ausprobieren etc. Das mit den Strings, char und Adresszeiger leuchtet mir noch nicht ganz ein. Nur das man String aus Speichergründen vermeiden sollte. Außerdem weiss ich, das ein char ein Array ist.
Um meine Zuordnung zu realisieren, brauche ich ein mehrdimensionales Array wie:

const char* Geburtstage= { “12.12.1980#Arduino#”,
“02.05.1995#Anna#”};

Außerdem:

const int NUMSTRINGS = sizeof(Geburtstage) / sizeof(Geburtstage[0]);
char searchString = "“12.12.1980#Arduino#”;

void name_zu_Geburtstag_suchen()
{
  int index = findString(searchString);
  if (index >= 0)
  {
    Serial.print(F("Index: ")); Serial.println(index);
    Serial.print(F("String: ")); Serial.println(Geburtstage[index]);
  }
  else
    Serial.println(F("String not found"));
}

int findString(const char* str)
{
  for (int i = 0; i < NUMSTRINGS; i++)
  {
    if (strcmp(str, Geburtstage[i]) == 0)
      return i;
  }

  return -1;
}

diesen Code habe ich beim Lesen gefunden und getestet. Erfunktioniert aber nur, wenn ich
nach der gesamten Zeichenkette suche. Ich will aber nur nach dem Datum suchen und dann
von dieser Zeichenkette den Rest(den Namen) anzeigen lassen.
Irgendwie gibt es ein Befehl, der ein bestimmtes Zeichen sucht aber wie weiter?

Gruss

Lars

Irgendwie gibt es ein Befehl

Es gibt in C/C++ keine Befehle. Sondern Funktionen und Methoden.

Eine Liste und Beispiele mit den Standard C String Funktionen findet man leicht mit Google. Deshalb ist es auch wichtig die Dinge richtig zu benennen. Dann kann man besser suchen

strstr() sucht nach Teil-Strings irgendwo im String:
http://www.cplusplus.com/reference/cstring/strstr/

strncmp() vergleicht N Zeichen am Anfang des Strings:
http://www.cplusplus.com/reference/cstring/strncmp/

Man kann auch den String erstmal mit strtok() Teilen. Dann geht auch strcmp():
strtok - C++ Reference
Letzteres ist in deinem Fall die beste Lösung, da du dann gleich nur den Teil dahinter hast

Du suchst strstr. Ansonsten schau zu Zeichenketten mal hier.

Gruß Tommy

Tommy56:
Du suchst strstr.

Das geht, aber ich glaube strtok() + strcmp() ist besser. Er will nach dem Datum suchen und den Teil-String dahinter ausgeben

Ja, wenn er immer konstante Trennzeichen hat, ist das effektiver.
Am Besten das # am Ende noch weglassen.

Gruß Tommy

strtok() zerstört die durchsuchte Zeichenkette und ist insofern wohl eher ungeeignet.

Mhh, das stimmt leider :frowning:

Ja, stimmt. Da es hier ja die gespeicherten Daten sind, die weiterhin Verwendung finden also entweder mit einer Kopie arbeiten oder doch strncmp bzw. strstr.

Gruß Tommy

Hallo zusammen,

danke für eure Bemühungen. Ich habe mir die Seite www.wikinger-tommy.de öfters angesehen und nach dem Verstehen(und probieren :slight_smile: ) bin ich zu folgendem Ergebnis gekommen:

const char* Geburtstage[]= { "12.12.1980Arduino  ", 
                                            "02.05.1995Anna"};

const int NUMSTRINGS = sizeof(Geburtstage) / sizeof(Geburtstage[0]);
char searchString[] = "02.05.1995";


void name_zu_Geburtstag_suchen()
{
  for (int i = 0; i < NUMSTRINGS; i++)
  {
    char* datum = strstr(Geburtstage[i],searchString);
    char* Name= (datum+10);
    Serial.println(Name);
  } 
}

Ich suche in jedem Eintrag, ob das Datum übereinstimmt. Dem Poiter für Datum addiere ich 10(für’s Datum) und erhalte die Startadresse des Namens. Außerdem habe ich das Speicherformat geändert:

const char* Geburtstage= { "12.12.1980Arduino ",
“02.05.1995Anna”};

Ich habe die # weggelassen. Fand ich so einfacher. Bei der seriellen Ausgabe ist mir aufgefallen:
wenn ich nur Serial.print anstatt Serial.println wird nach dem Namen ein umgekehrtes Fragezeichen und ein Quadrat angezeigt.

Vielen Dank!

Gruss

lars

Das würde ich etwas anders schreiben (ungetestet):

void name_zu_Geburtstag_suchen()
{
bool gefunden = false;
  for (int i = 0; i < NUMSTRINGS; i++)
  {
    char* datum = strstr(Geburtstage[i],searchString);
    if (datum) { // gefunden
      gefunden = true;
      char* Name= (datum+10);
      Serial.println(Name);
    }
  }
   if (!gefunden) Serial.println("Nicht gefunden");
}

Gruß Tommy

aber eigentlich, warum hältst du die Daten überhaupt in einer Variable?
Teile es in eine Struktur auf, dann brauchst es später auch nicht auseinanderteilen.
keine Trennzeichen, keine 10 Byte für ein Datum,
imho wären das alles Vorteile wenn du das Trennen würdest.

Hallo,
warum ich das nicht in eine Struktur einbinde? Weil mir erst jetzt bewußt wird, das es sowas gibt. :slight_smile:

Also so:

typedef struct
{
char Geburtsdatum[10];
char Name[15];
}VIP;

VIP Daten[20];
sprintf(Daten[0].Geburtsdatum, "12.12.2000");
sprintf(Daten[0].Name, "Arduino");
sprintf(Daten[1].Geburtsdatum, "08.29.1980");
sprintf(Daten[1].Name, "Arduino");

Ist das soweit Richtig? Und dann später mit dem Index im Geburtsdatum suchen?

Gruss

lars

Ja. Aber bitte wieder in Codetags setzen.

Gruß Tommy

fast würde ich sagen.

Datum würde ich als 20001212 speichern, dass passt in ein uint32_t. Das kannst eh wieder bei der Ausgabe so drehen wie du willst.

Oder man kann auch ein struct in ein struct packen. Mach ein struct für dein Datum bestehend aus yyyy mm dd uint16_t, byte, byte ... wären auch nur 4 byte. Schau dir mal eine timelib, RTC ... an wie die eine Datumsvariable machen.

Die Instanz würde ich nicht Daten nennen. sind nicht alle deine Variablen Daten?

Wie wärs mit person oder termin - dann passt das auch für deinen Arduino. Auf alle Fälle die Variable wieder klein geschrieben.

const machen kannst das ganze dann auch wieder.

Dann musst Du aber bei der Eingabe wieder umwandeln. Struct ja, aber 2 mal Zeichenkette (mit oder ohne Punkte) würde ich für eine einfache Suche doch eher nehmen. Sonst kann ich ja gleich als UNIX-Timestamp speichern.

Gruß Tommy

POC:

/*
  by noiasca
*/


struct Vip
{
  const uint32_t datum;
  const char name[15];
};

Vip vip[]
{
  {20001212, "Arduino"},
  {19800829, "Anna"},
  {20110102, "Berta"},
    {500504, "Cäsar"}   // Aufpassen, keine Vornullen, da Oktal!
};

const byte vips = sizeof(vip) / sizeof(vip[0]);

int index_zu_Geburtstag_suchen(uint32_t suchdatum, bool exakt = true)
{
  for (byte i = 0; i < vips; i++)
  {
    if (exakt)
    {
      if (suchdatum == vip[i].datum)
      {
        return i;
      }
    }
    else
    {
      if (suchdatum % 10000UL == vip[i].datum % 10000UL)
      {
        return i;
      }
    }
  }
  return -1;
}




void setup() {
  Serial.begin(115200);
  Serial.println(F("\nStart"));

  int result = -1;
  result = index_zu_Geburtstag_suchen(19800829);
  if (result >= 0) Serial.println(vip[result].name);
  else Serial.println(F("nicht gefunden"));

  result = index_zu_Geburtstag_suchen(20110102);
  if (result >= 0) Serial.println(vip[result].name);
  else Serial.println(F("nicht gefunden"));

  result = index_zu_Geburtstag_suchen(20111234);
  if (result >= 0) Serial.println(vip[result].name);
  else Serial.println(F("nicht gefunden"));

  uint32_t heute = 20200504;
  result = index_zu_Geburtstag_suchen(heute, false);
  if (result >= 0) Serial.println(vip[result].name);
  else Serial.println(F("heute hat niemand Geburtstag"));
}

void loop() {

}

21:33:30.931 → Start
21:33:30.931 → Anna
21:33:30.931 → Berta
21:33:30.931 → nicht gefunden
21:33:30.931 → Cäsar

Du musst mir nicht zeigen, was geht. Das weis ich selbst.

Die Frage war eher wieviel Abstraktion von der normalen Schreibweise des Datums ist für die Anwendung des TO sinnvoll.

Gruß Tommy

Tommy56:
Die Frage war eher wieviel Abstraktion von der normalen Schreibweise des Datums ist für die Anwendung des TO sinnvoll.

Da stimme ich dir völlig zu.
Bei meinen Geburtstagssuchen spielt z.B. das Jahr keine Rolle.

Das würde dann wieder für strncmp mit Länge vom Suchstring sprechen, um auch nTeile ohne Jahr zu finden.

Gruß Tommy

Hallo Lars,
wenn Du bei den C-Strings bleiben willst (ich würde struct in struct bevorzugen):

typedef struct
{
    char Geburtsdatum[10];
    char Name[15];
}VIP;

VIP Daten[20];
sprintf(Daten[0].Geburtsdatum, "12.12.2000");

M.E. nur fast richtig - denn der Datumsstring ist ein Zeichen zu kurz.
C-Strings werden immer mit einem Null-Byte abgeschlossen. Soweit mir bekannt schreibt sprintf das ebenfalls. Du hast aber schon 10 Zeichen incl. der beiden Punkte - also die Struktur ändern:

typedef struct
{
    char Geburtsdatum[11];
    char Name[15];
}VIP;

Gruß Walter