String mit VIEEEELEN Strings per ODER vergleichen ??

Hallo,

ich bekomme per serieller Schnittstelle einen String. Diesen möchte ich ( per ODER) mit VIEELEN Möglichkeiten vergleichen. Ca. 150 Möglichkeiten!!

Wenn der String enthalten ist, soll er gelöscht werden. Wenn nicht, soll er erhalten bleiben.

Wie kann ich so eine Masse Vergleiche programmieren?? Und das auch noch übersichtlich?
Ich dachte an ein separates "string.h"-File, welches ich dann einbinde.

Nur wie programmiere ich sowas??

Vielen Dank im Voraus.
Gruß, Andreas

DL1AKP:
Wie kann ich so eine Masse Vergleiche programmieren?? Und das auch noch übersichtlich?

Mit einer for-Schleife, deren Laufindex von 0 bis 149 läuft und dabei je einen String mit einem anderen String in einem String-Array vergleicht? Warum sollte ein Array nicht übersichtlich sein?

Allerdings stellt sich die Frage, ob Du das tatsächlich so wie beschrieben machen möchtest. Weil wenn die Strings alle gleichzeitig im RAM-Speicher gehalten werden, belegen sie dort satt Speicher. Bei einer angenommenen Stringlänge von 10 Zeichen mindestens 11 Bytes, macht bei 150 Strings also 11*150 Bytes = ca. 1650 Bytes für das String-Array. Ein UNO hat beispielsweise insgesamt nur 2 KB RAM, und wenn Du diverse Dinge am Laufen hast wie z.B. Serial, sind davon vielleicht nur ca. 1700 Bytes RAM frei und es fragt sich: Willst Du den gesamten RAM mit Deinen Strings zuknallen und das war's dann? Oder verwendest Du einen MEGA mit 8 KB RAM?

Oder möchtest Du irgendwas intelligenteres mit weniger Verbrauch von RAM-Speicher programmieren? Z.B. die Strings als Konstanten im Flash-Speicher statt im RAM-Speicher ablegen, und statt die Strings im Programmverlauf tatsächlich zu löschen, im RAM nur ein Array von "Merk-Bits" zu verwalten, in denen mit 0 oder 1 vermerkt ist, ob der String als gelöscht oder vorhanden zu betrachten ist? RAM-Verbrauch für 150 Merk-Bits = 150/8= 18,75 = 19 Bytes?

Je nachdem, ob Dein Programm also 1650 Bytes RAM verbrauchen darf, oder nur 1650 Bytes Flash plus 19 Bytes RAM zur Verwaltung der Datenstrukturen, richtet sich die Programmierung.

Außerdem stellt sich mir insgesamt die Frage, wofür das wohl sein soll und warum Du so viele Strings zu verwalten hast? Das muß wohl für so eine Art Quiz-Controller sein: Ein Moderator liest die Fragen vor und am Arduino sollen die Antworten eingegeben und auf Richtigkeit geprüft werden.

Hallo jurs,

danke für die Infos. An den RAM-Verbrauch habe ich nicht gedacht... Nein, es ist ein UNO, kein Mega.
Also muss ich da wohl sparsam sein.

Zur Anwendung: Mein (primär)-Hobby ist Amateurfunk. Über ein Funkmodem kommen über die serielle Schnittstelle (u.a.) unterschiedliche Amateurfunk-Rufzeichen (wie meines zB. DL1AKP, DB0ERF, DG4ES... etc.) Nun will ich abprüfen, ob des seriell empfangene Rufzeichen in der Liste ist, oder nicht. Wenn ja, soll der String gelöscht werden und eine Funktion ausgeführt werden.
Die auszuführende Funktion ist immer dieselbe.

Es geht also nur darum, herauszufinden: Ist das seriell empfange Rufzeichen in der Liste der Rufzeichen enthalten. Diese Liste ist ca. 100 - 150 Rufzeichen.
Hoffe, das ist als Erklärung verständlich?

Grüße, Andreas

DL1AKP:
Es geht also nur darum, herauszufinden: Ist das seriell empfange Rufzeichen in der Liste der Rufzeichen enthalten. Diese Liste ist ca. 100 - 150 Rufzeichen.
Hoffe, das ist als Erklärung verständlich?

OK, um viel RAM-Speicher zu sparen und es nicht zu kompliziert zu machen, könntest Du dann eine Datenstruktur wie folgt im Programm verwenden: Die Rufzeichen-Strings im Flash-Speicher und eine Stringpointer-Liste im RAM nach diesem Schema:

#include <avr/pgmspace.h>

const char rz0[] PROGMEM = "DL1AKP";
const char rz1[] PROGMEM = "DB0ERF";
const char rz2[] PROGMEM = "DG4ES";

const char* rufzeichen[] = {
    rz0,
    rz1,
    rz2,
};
#define NUMRUFZEICHEN (sizeof(rufzeichen)/sizeof(rufzeichen[0]))

char* getFlashRufzeichen(const char* str)
{
  static char rz[10]; // für Rufzeichen bis max. 9 Zeichen Länge
  int i=0;
  do
  {
    rz[i]=pgm_read_byte(str+i);
    i++;
  } while (rz[i-1]!='\0');  
  return rz;
}


void printAlleRufzeichen()
{
  for (int i=0;i<NUMRUFZEICHEN;i++)
  {
    if (rufzeichen[i]!=NULL)
    {
      Serial.println(getFlashRufzeichen(rufzeichen[i]));
    }
  }
  Serial.println();
}

void setup() {
  Serial.begin(9600);
  Serial.println("Liste vorher:");
  printAlleRufzeichen();

  rufzeichen[1]=NULL; // Rufzeichen mit index 1 löschen
  Serial.println("Ein Rufzeichen entfernt");
  Serial.println("Liste nachher:");
  printAlleRufzeichen();

}

void loop() {
}

Dabei werden die einzelnen Rufzeichenstrings rz0, rz1, rz2 etc. im Flash-Speicher (PROGMEM) abgelegt und belegen dort keinen RAM.

Zusätzlich gibt es ein Stringpointer-Array "rufzeichen[]" im RAM, das pro Rufzeichenpointer 2 Bytes belegt. Bei 150 Rufzeichen also 300 Bytes RAM-Verbrauch.

Das Demoprogramm gibt die komplette Rufzeichenliste einmal auf Serial aus, löscht dann ein Rufzeichen und gibt die Rufzeichenliste nach dem Löschvorgang nochmals aus (dann ohne das gelöschte Rufzeichen).

Um das Löschen zu vermerken, setzt Du einfach den Stringpointer auf den Wert NULL. Und beim Durchlaufen einer Schleife kannst Du dann durch Auswerten der Bedingung

if (rufzeichen[i]!=NULL)

feststellen, ob der String gelöscht ist oder nicht.

Wobei die Strings an sich nicht gelöscht werden können, da sie nur als Konstanten im Flash-Speicher existieren und dort dann keinen RAM-Speicher verbrauchen.

Zum Stringvergleich mit PROGMEM-Stringpointern kann die Funktion "strcmp_P" verwendet werden.

Wenn die Anwendung nicht zeitkritisch ist, könntest Du in einer Schleife immer 150 Stringvergleiche machen, um festzustellen, ob sich ein bestimmtes Rufzeichen in der Liste befindet. Sortierung egal.

Wenn die Anwendung zeitkritisch ist, könnte man mit einem optimierten Algorithmus viel Suchzeit einsparen, wenn die Rufzeichenliste, in der ein Rufzeichen gesucht werden soll, alphabetisch sortiert vorliegt. Bei 150 Einträgen könnte die Suche dann so organisiert werden, dass nur maximal 8 statt 150 Stringvergleiche notwendig sind.

Da wo es kursiv wird, fehlt immer ein [ i ] :wink:

Schöne Lösung für die "Löschen" Aufgabenstellung, wenn das Wiederherstellen durch den Reset-Taster erledigt werden kann/soll.

michael_x:
Da wo es kursiv wird, fehlt immer ein [ i ] :wink:

Stimmt. Habe ich gerade mal gefixed.

michael_x:
Schöne Lösung für die "Löschen" Aufgabenstellung, wenn das Wiederherstellen durch den Reset-Taster erledigt werden kann/soll.

Aus der Beschreibung des Threadstarters ging nicht hervor, ob die Liste nach einem Reset wieder komplett sein soll, oder ob er sich die Löschungen irgendwie dauerhaft (z.B. im EEPROM) über den Reset hinaus merken möchte, so dass die Löschung 'dauerhaft' wird. Könnte man aber natürlich bei Bedarf auch zusätzlich machen: Jeweils den Stringpointer löschen und einen Merker im EEPROM speichen. Bei jedem Programmstart das EEPROM auslesen und gelöschte Einträge gleich beim Programmstart wieder auf NULL setzen.

Per strcmp_P() kann man einfach einen String im RAM mit einem String im Flash vergleichen

Hallo jurs,

nochmals vielen Dank für die Lösungsansätze. Das mit den Pointern hatte ich noch nie benutzt.
Werde mich da mal genau einlesen.

Und ja, nach dem RESET/Neustart soll alles wieder vorhanden sein. Die Löschung also nicht dauerhaft.
Zeitkritisch ist es nicht! Selbst wenn das ein paar Sekunden dauert, ist das unkritisch.

Werde das mal mit ein paar Rufzeichen testen und dann erweitern.

Schöne Grüße, Andreas

Hallo jurs,

muss mich nochmal melden. Habe mich mal mit Deinem Code beschäftigt.
Da ich aus dem Elektronik-Sektor komme (analoge Elektronik, Amateurfunk, Sendetechnik), habe ich mir die Programmierung nur autodidaktisch erarbeitet. Daher fehlt mir wohl auch so etwas die "Einsicht" in die dortigen Vorgänge.
Daher erschliesst sich mir die Arbeitsweise dieser Arrays und Pointer nicht.

Ich wollte es jetzt einfach mal so machen, das ich einen String test_call habe. Diesen wollte ich nun vergleichen mit den Werten in dem Array. Wenn er enthalten ist, soll ein Flag gesetzt werden.

Habe ich jetzt so gemacht:

void printAlleRufzeichen()
{
  for (int i=0;i<NUMRUFZEICHEN;i++)
  {
    if (rufzeichen[i]!=NULL)
    {
      Serial.println(getFlashRufzeichen(rufzeichen[i]));
      flash_call = getFlashRufzeichen(rufzeichen[i]);
      
      if (flash_call == test_call){
         Serial.println("test_call enthalten!");
         call_flag = true; //Flag setzen
         Serial.println(call_flag);
         }
     
    }
  }
  Serial.println(); // **************************
  
}

Diese Methode rufe ich dann immer auf bei Bedarf.
Ist das Flag gesetzt, wird mein serieller String gelöscht und das Flag zurückgesetzt.
Soweit so gut, oder?

Grüße, Andreas

mal abgesehen davon, dass flashcall und testcall nicht definiert sind,
kann man zwei Strings nicht mit == vergleichen.

such mal nach strcmp

DL1AKP:
Daher erschliesst sich mir die Arbeitsweise dieser Arrays und Pointer nicht.

Ja, irgendwie scheinen Dir im Umgang mit C-Strings die Grundlagen völlig zu fehlen.

Auch, dass Strings nicht mit == verglichen werden, sondern dazu eine Funktion wie strcmp() oder strcmp_P() verwendet werden muss, scheint Dir völlig fremd zu sein.

Und dann fängst Du auch gleich damit an, so viele Strings verarbeiten, dass das mit so wenig RAM-Speicher nur mit Klimmzügen über den PROGMEM-Flashspeicher möglich ist, was das gesamte String- und Stringpointer-Handling extrem verkompliziert...

Ich habe Dir mal ein überarbeitetes Testprogramm gemacht:

#include <avr/pgmspace.h>

const char rz0[] PROGMEM = "DL1AKP";
const char rz1[] PROGMEM = "DB0ERF";
const char rz2[] PROGMEM = "DG4ES";

const char* rufzeichen[] = {
    rz0,
    rz1,
    rz2,
};
#define NUMRUFZEICHEN (sizeof(rufzeichen)/sizeof(rufzeichen[0]))

char* getFlashRufzeichen(const char* str)
{
  static char rz[10]; // für Rufzeichen bis max. 9 Zeichen Länge
  int i=0;
  do
  {
    rz[i]=pgm_read_byte(str+i);
    i++;
  } while (rz[i-1]!='\0');  
  return rz;
}


void printAlleRufzeichen()
{
  for (int i=0;i<NUMRUFZEICHEN;i++)
  {
    if (rufzeichen[i]!=NULL)
    {
      Serial.println(getFlashRufzeichen(rufzeichen[i]));
    }
  }
  Serial.println();
}

char* readSerialRufzeichen()
{
  static char str[11]; // max. Rufzeichenlänge 10 + '\0'
  static int count=0;
  if (Serial.available())
  {
    if (count==0) memset(str,0,sizeof(str)); // String löschen
    while (Serial.available())
    {
      char c=Serial.read();
      if (isalnum(c) && count<sizeof(str)-1) // alphanumerischen Zeichen?
      {
        str[count]=c;
        count++;
      }
      else if (!isalnum(c) && count>0)
      {
        count=0;
        return str;
      }
    }
  }
  return NULL;  
}

int searchRufzeichen(char* str)
{
  for (int i=0;i<NUMRUFZEICHEN;i++) // gehe alle Rufzeichen durch
  {
    if (rufzeichen[i]!=NULL) // wenn das Rufzeichen nicht bereits gelöscht ist
    {
      if (strcmp_P(str,rufzeichen[i])==0)  // Stringvergleich mit Flash-Stringpointer auf identische Strings
      {
        rufzeichen[i]=NULL; // Rufzeichen aus der Liste löschen
        return i; // Index zurückliefern
      }
    }
  }
  return -1;
}

void testeRufzeichen(char* str)
{
  int i=searchRufzeichen(str);
  if (i>=0)
  {
    Serial.print("Rufzeichen wurde gefunden und geloescht: ");
    Serial.println(str);
  }
  else
  {
    Serial.print("Nicht vorhandenes Rufzeichen: ");
    Serial.println(str);
  }
}

void setup() {
  Serial.begin(9600);
  Serial.println("Liste aller Rufzeichen:");
  printAlleRufzeichen();
  Serial.println("Warte auf Rufzeichen vom seriellen Monitor:");
}

void loop() {
  char* rufz= readSerialRufzeichen();
  if (rufz!=NULL) testeRufzeichen(rufz);
}

In diesem Testprogramm kannst Du jetzt nicht nur die Datenstrukturen für Deine Rufzeichen anlegen, sondern in der loop() Funktion läuft auch eine Auswertung der seriellen Schnittstelle auf empfangene Zeichen, und wenn irgendwas empfangen wurde wird die Funktion testeRufzeichen() aufgerufen, die gefundene Rufzeichen einerseits aus der Liste löscht als auch eine Meldung ausgibt, ob das Rufzeichen gefunden und gelöscht wurde oder nicht.

Dieses Programm erkennt jetzt allerdings nur Rufzeichen in exakt derselben Groß-/Kleinschreibung, wie sie in der Liste stehen. Wenn Du die Rufzeichen in Deiner Liste in Großschreibung einträgst, aber auch Rufzeichen in Kleinschreibung oder in gemischter Schreibweise erkannt werden sollen, wären Änderungen notwendig.

Das Kopieren von Strings aus dem Flash ins RAM geht auch mit strcpy_P(). Man kann es natürlich auch per Hand machen, aber in diesem Fall ist es sinnvoll die Funktionalität etwas zu verstecken, da er dann weniger Code verstehen muss.

Hallo Andreas,
als Anregung fiel mir ein, eine SD-Karte zu verwenden, die eine Datei mit den Rufzeichen enthält. Die Datei könnte am PC geändert werden, ohne das Programm neu in den Arduino übertragen zu müssen. Bei mir funktioniert eine 4 GByte Karte. Du könntest die Dateien mit den gelöschten Rufzeichen dann auch als Protokoll speichern.

Hallo nochmal,

ja, IHR habt Recht - mit den komplizierten String-Operationen komme ich nicht klar.
Da fehlt mir das Verständnis.
Ich stamme eben aus der "echten" Elektronik (Hardware-Eigenbau), da leuchtet es mir nicht ein warum HANS == Hans nicht funktionieren soll :grin:

Ich werde die komplizierten String-Pointer erst mal beiseite legen, bis mir das (irgendwann mal) einleuchtet.
Bis dahin lebe ich erst mal mit der Funktion ohne diese Erkennung.
Es ist für das Programm nicht essentiell, nur "komfortabel"...

Grüße, Andreas

DL1AKP:
da leuchtet es mir nicht ein warum HANS == Hans nicht funktionieren soll

Weil Strings in C ganz primitiv sind. Das sind nicht mehr als Arrays aus char mit einem NULL am Schluss um das Ende zu markieren. Und Array Variablen sind Zeiger auf das erste Element.

Wenn man also == macht, vergleicht man nur die zwei Zeiger (d.h. ob die Variablen auf den gleichen Speicher zeigen), aber nicht den Inhalt. Deshalb gibt es strcmp() (string compare) als Funktion.

Hallo Andreas,
so schön programmieren wie jurs, michael_x oder Serenifly kann ich als Anfänger nicht, aber ich bin ihnen auf den Fersen. So ist es mir gelungen, was "zusammenzuklicken". Leider habe ich die Links zu den Forumsbeiträgen versehentlich gelöscht :-[ . Ich hatte Dir ja den Vorschlag mit der SD-Karte gemacht, den ich nun mal probiert habe, ob sich das machen läßt.

Auf der SD-Karte habe ich mit dem PC eine Datei rz.txt mit 123 Zeilen und diesem Inhalt angelegt:

r00001
r00002
r00003
r00004
DL1AKP7
DB0ERF
DG4ES
r00005
r00006
r00007
r00008
r00009
r00010
...

Mangels Funke gebe ich die Rufzeichen "DG4ES", "DL1AKP7", "DB0ERF" und "xxx" am seriellen Monitor ein. Ausgabe am seriellen Monitor:

Test_Forum.ino Apr 3 2015 14:38:01
SD-Karte OK!
Datei rz.txt geoeffnet.
Gefunden: DG4ES
Datei rz.txt geschlossen.
Datei rz.txt geoeffnet.
Gefunden: DL1AKP7
Datei rz.txt geschlossen.
Datei rz.txt geoeffnet.
Gefunden: DB0ERF
Datei rz.txt geschlossen.
Datei rz.txt geoeffnet.
Keine Uebereinstimmung gefunden.
Datei rz.txt geschlossen.

Die vom UNO geänderte Datei sieht dann so aus:

r00001
r00002
r00003
r00004



r00005
r00006
r00007
r00008
r00009
r00010
...

Der Sketch ist nicht im Detail getestet!

Danke für die schöne Anregung! :grin:

rz.txt (860 Bytes)

Test_Forum.ino (2.79 KB)