String zählen

jensel

Well-known member
Beiträge
61
Punkte Reaktionen
0
Zuviel Werbung?
->Hier kostenlos registrieren
Hallo,

ich habe ein Problem.

Folgende Situation ich möchte in einen String mit einer länge von 100 Zeichen eine Anzahl Zählen.
Das heißt wenn die Zeichenfolge = FCG möchte ich ein Ergebniss (INT) 1 haben, wenn es 2 mal vorkommt eine 2 und so weiter.

Ich möchte dies in SCL machen. Weiß aber nicht so genau wie ich anfangen soll. Dachte da so an neh FOR Schleife oder ist das quatsch.

Würde mich freuen wenn mir einer helfen kann.

Danke schon mal dafür.
 
OP
J

jensel

Well-known member
Beiträge
61
Punkte Reaktionen
0
String

Hallo,

ich habe ein Problem.

Folgende Situation ich möchte in einen String mit einer länge von 100 Zeichen eine Anzahl Zählen.
Das heißt wenn die Zeichenfolge = FCG möchte ich ein Ergebniss (INT) 1 haben, wenn es 2 mal vorkommt eine 2 und so weiter.

Ich möchte dies in SCL machen. Weiß aber nicht so genau wie ich anfangen soll. Dachte da so an neh FOR Schleife oder ist das quatsch.

Würde mich freuen wenn mir einer helfen kann.

Danke schon mal dafür.
 

MasterOhh

Well-known member
Beiträge
1.582
Punkte Reaktionen
397
Zuviel Werbung?
->Hier kostenlos registrieren
Na da bietet sich doch die Funktion "Find" an. Mit der kannst du einen String auf das vorhandensein eines anderen Strings untersuchen. "Find" liefert dabei die Position zurück an der sich dein gesuchter String befindet. Ist Find = 0 wurde nichts gefunden.

Da "Find" aber nur bis zu ersten Treffer sucht, du aber wissen willst ob die Zeichenfolge mehrfach vorkommt, musst du etwas basteln. Du kannst z.B. den ersten Teil des zu durchsuchenden Strings (einschließlich des ersten Treffers) löschen und es dann nochmal mit "Find" versuchen. Das machst du so oft bis entweder eine 0 zurück kommt oder du am Ende des Strings angekommen bist.

Schau dir einfach mal die String- Funktionen für SCL an.
 

Ralle

Supermoderator
Teammitglied
Beiträge
15.103
Punkte Reaktionen
3.820
Kommt ein wenig darauf an.
Wenn nicht zu viele Vorkommen der Zeichenkette im String sind, bietet sich eine Schleife an. Wenn es dann zuviel Zuykluszeit verbrät, muß man das Ganze auf mehrere Zyklen aufteilen.
Wenn du z.Bsp. keine Schleife machst und in jedem Zyklus 1 Mal suchst, bis su durch bist, kann das ein deinem Beispiel im Extremfall 33 mal sein, bei 10ms sind das dann 330ms, bis ein Ergebnis vorliegt.

Zum Weg:
1. Du kannst die in SCL vorhandenen Stringfunktionen nutzen.

Code:
[FONT=courier new]1. Find:  Suchen der Zeichenkette, die Position des 1. Vorkommens wird dir ausgegeben
2. Zähler hochzählen
3. Right: mit der Position der Fundstelle + Länge des Suchstrings als L und dem String als Input 
          liefert es einen neuen String ab dem Ende des 1. Vorkommens des Suchstrings
4. Len:   die Länge des zurückgegebenen Strings prüfen. Ist die Länge < 3, dann bist du fertig, ist sie größer, 
          dann in der nachsten Runde wieder bei 1 anfangen, aber als Inputstring den, in 3 zurückgelieferten Reststring verwenden. [/FONT]

2. Du kannst das selbst machen, in einer Schleife von 1-100

Code:
[FONT=courier new]1. Zeiger für die "Zeichenposition" auf 1 stellen
2. "Anzahl der Vorkommen" auf 0 stellen

Hier beginnt die Schleife
3. Zeichen an "Zeigerposition" des Strings lesen und vergleichen, ob 'F' darin steht
   wenn nein, weiter mit 7.  
   wenn ja, dann  
4. Zeichen an "Zeigerposition" + 1 des Strings lesen und vergleichen, ob 'C' darin steht 
   wenn nein, weiter mit 7.
   wenn ja dann 
5. Zeichen an "Zeigerposition" + 2 des Strings lesen und vergleichen, ob 'G' darin steht  
   wenn nein, dann weiter mit 7.
   wenn ja, dann
6. Zeichenfolge wurde gefunden, "Anzahl der Vorkommen" um 1 erhöhen,
   "Zeichenposition" um 2 erhöhen, dann weiter mit 7.

7. "Zeichenposition" um 1 erhöhen
8. Prüfen ob "Zeichenposition" > 97
   wenn nein, dann Sprung zu 3.
   wenn ja, dann Schleife fertig, Ergebnis steht in Anzahl der Vorkommen
Hier endet die Schleife
[/FONT]

Das wäre mal die Vorgehensweise.

Wenn du einen SCL-Baustein anlegst, kannst du im SCL-Editor die Hilfe aufrufen und findest über die Suche detaillierte Hilfen zu Find, Right und auch zu Schleifen.
 
Zuletzt bearbeitet:

Ralle

Supermoderator
Teammitglied
Beiträge
15.103
Punkte Reaktionen
3.820
@jensel

Bitte ein Thema nur einmal einstellen, ich habe die deinen beiden mal zusammengeführt!
 
OP
J

jensel

Well-known member
Beiträge
61
Punkte Reaktionen
0
Zuviel Werbung?
->Hier kostenlos registrieren
Dankefür die schnellen antworten

Hi danke für die schnelle Hilfe hab mich gefreut.
@ralle wollte den 2 löschen hab es aber nicht hin bekommen !!

Ich finde deine 1. Idee gut mit dem zähler hab aber noch neh frage dazu ich würde das jetzt so machen für die erste prüfung aber wie mache ich weiter ??

A:=FIND(STRING1,'FCG');
B:=RIGHT(STRING1,A);
C:=LEN(STRING1)
IF B < 1 THEN ZAHL:= ZAHL + 1;
??
C < 3 THEN ???

muß ich jetzt die Anweisungen 66 mal schreiben??
 

Ralle

Supermoderator
Teammitglied
Beiträge
15.103
Punkte Reaktionen
3.820
ok, ich hab das mal ausprobiert, ;)

Right ist hier nicht das Richtige, wir nehmen Delete!

Code:
FUNCTION_BLOCK FB104
VAR_INPUT
    Start               : BOOL;
    String1             : STRING;
END_VAR
VAR_OUTPUT
    Anzahl              : INT;
    Ready               : BOOL;
END_VAR
VAR_IN_OUT  
END_VAR
VAR_TEMP
    // temporäre Variablen
    A, B             : INT;
END_VAR
VAR
    // statische Variablen
    String2             : STRING := '';
    FPStart_intern      : BOOL;
    HM_FPStart_intern   : BOOL;
    Start_intern        : BOOL;
END_VAR


FPStart_Intern := Start AND NOT HM_FPStart_intern;
HM_FPStart_intern := Start;  


//Startwerte setzen
IF FPStart_intern THEN
    STRING2 := '';              //STRING2 initialisieren
[COLOR=#008000]//    STRING2 := String1;         //String 1 in String 2 kopieren[/COLOR][COLOR=#ff0000]
[/COLOR][COLOR=#ff0000]    STRING2 := CONCAT(IN1 := String1    // IN: STRING[/COLOR]
[COLOR=#ff0000]                     ,IN2 := ' '        // IN: STRING[/COLOR]
[COLOR=#ff0000]                     );                 // STRING[/COLOR]


    Anzahl := 0;                //Anzahl auf Startwert;
    Ready := False;             //Fertigmeldung zurücksetzen
    Start_intern := true;       //Start setzen
END_IF;
    
IF Start_intern THEN
    //Suchen des Suchstrings
    A := FIND(IN1 := STRING2    // IN: STRING
             ,IN2 := 'FCG'      // IN: STRING
             ); // INT
    IF A <> 0 THEN
        //von Position 1 bis zum Fundort + 2 (Fundort ist Zeichen 1 des Suchstrings) löschen 
        String2 := DELETE(IN := String2 // IN: STRING
                         ,L := (A+2)    // IN: INT
                         ,P := 1        // IN: INT
                         );             // STRING
         
        Anzahl := Anzahl + 1;
        //Stringlänge bestimmen
        B := LEN(S := STRING2 // IN: STRING
                ); // INT
        IF B < 3 THEN
            //endgültiges Ende der Suche, da String kürzer als die 3 gesuchten Zeichen
            Ready := TRUE;
            Start_Intern := False;
        END_IF;
    ELSE
        //endgültiges Ende der Suche, da Suchstring nicht gefunden
        Ready := TRUE;
        Start_Intern := False;
    END_IF;
END_IF;
             
    
    
END_FUNCTION_BLOCK

Achtung, du brauchst aus der Standard-Library/IEC-Funktion

FC21 - LEN
FC11 - FIND
FC4 - DELETE
FC2 - CONCAT

den FB wie folgt aufrufen:

Code:
      CALL  FB   104 , DB104
       Start  :=M1.0
       String1:=DB105.String1
       Anzahl :=MW2
       Ready  :=M1.1

Im DB105 habe ich einfach eine Variable vom Typ String angelegt.
Wenn es denn unbedingt 100 Zeichen sein müssen, dann lege einen String[100] an, String1 und String2 im FB104 dann bitte auch so ändern.

PS: Tigerente hat mich darauf hingewiesen, dass das letzte 'FCG', das in einem String 'FCG' oder 'FCG....FCGFCG' steht nicht mitgezählt wird.
Daher habe ich die grün markierte Zeile, durch die rot markierten ersetzt. Durch das Anhängen eines Leerzeichens funktioniert auch dieser Sonderfall, offenbar ist da ein Fehler in der Siemens-Bibliothek bei "Find"!
 
Zuletzt bearbeitet:

Pipboy

Well-known member
Beiträge
195
Punkte Reaktionen
44
Eigentlich schon frech, dass der Find nicht ab Position x suchen kann.

Mich würde mal interessieren, ob die Schleife schneller ist. In etwa:
Code:
found := 0;
FOR #i := 1 TO LenString-LenSearch DO
    FOR #j := 1 TO LenSearch DO
        IF SearchIn[i+j-1] <> SearchFor[j] THEN
            Exit;
        END_IF;
    END_FOR;
    IF j = LenSearch THEN
        found = found +1;
    END_IF;
END_FOR;
 

Thomas_v2.1

Well-known member
Beiträge
8.822
Punkte Reaktionen
2.705
Zuviel Werbung?
->Hier kostenlos registrieren
Das wäre ja die andere Lösung die Ralle vorgeschlagen hat.
Zugriff auf einzelne Zeichen eines Strings mit Arrayindex ist aber nicht direkt möglich. Dazu bräuchte man eine AT-Sicht auf den String.
Die Variante mit den Stringfunktionen ist auf jeden Fall ein richtiger Zykluszeitvernichter, zumindest wenn man sie mit den mitgelieferten Funktionen umsetzen will.
 

Ralle

Supermoderator
Teammitglied
Beiträge
15.103
Punkte Reaktionen
3.820
Das wäre ja die andere Lösung die Ralle vorgeschlagen hat.
Zugriff auf einzelne Zeichen eines Strings mit Arrayindex ist aber nicht direkt möglich. Dazu bräuchte man eine AT-Sicht auf den String.
Die Variante mit den Stringfunktionen ist auf jeden Fall ein richtiger Zykluszeitvernichter, zumindest wenn man sie mit den mitgelieferten Funktionen umsetzen will.

Richtig, deshalb habe ich das ja auch nicht in einer For- oder While-Schleife gemacht, sondern pro Zyklus immer nur ein Vorkommen des Suchstrings gesucht. Es dauert also für 8 Vorkommen, 8 SPS-Zyklen.

@Pipboy

Diese Variante habe ich in ähnlicher Form auch schon einmal genutzt, den Zugriff habe ich dann nicht über eine AT-Ansicht (das kannte ich damals noch nicht), sondern über den direkten Zugriff in den DB mit dem String erledigt.

Ich denke auch, die Schleife wäre schneller.
 
Zuletzt bearbeitet:

Tigerente1974

Well-known member
Beiträge
1.816
Punkte Reaktionen
290
@Ralle: Aus Interesse hatte ich gestern an der Lösung gebastelt. Zwar mit einer WHILE-Schleife aber sonst genauso mit DELETE etc...

Ich bin nicht sicher, ob ich da was falsch gemacht habe. In einem String der aus 'FCGFCGFCG' bestand, hatte ich zum Schluss noch einen STRING 'FCG' übrig, den er nicht mehr gezählt hat.

Ich habe dann noch folgendes ausprobiert:

Code:
STRING2 := 'FCG'
A := FIND(IN1 := STRING2,IN2 := 'FCG');

A war in diesem Fall = 0. Kann das sein?
 

Thomas_v2.1

Well-known member
Beiträge
8.822
Punkte Reaktionen
2.705
Zuviel Werbung?
->Hier kostenlos registrieren
Das wäre die Variante in pipboy Stil.
Waren noch zwei Änderungen notwendig.
In der äußeren Schleife +1 ergänzt und
nach der inneren Schleife Vergleich auf "j > LenSearchFor" da nach Ende der For-Schleife der Index größer als die Abbruchbedingung ist.

Code:
FUNCTION StringSuchN : INT
//
// Gibt die Anzahl der Vorkommen von String "SearchFor" in "SearchIn" zurück
//
CONST
    STR_MAX := 100;  // Stringlänge
END_CONST

VAR_INPUT
    SearchIn : STRING[STR_MAX];     //String der durchsucht werden soll
    at_SearchIn AT SearchIn : STRUCT
        len_max : BYTE;
        len_act : BYTE;
        chars : ARRAY[1..STR_MAX] OF BYTE;
    END_STRUCT;
    SearchFor : STRING[STR_MAX];    //String dessen Vorkommen gesucht werden soll
    at_SearchFor AT SearchFor : STRUCT
        len_max : BYTE;
        len_act : BYTE;
        chars : ARRAY[1..STR_MAX] OF BYTE;
    END_STRUCT;
END_VAR

VAR_TEMP
    LenSearchIn : INT;
    LenSearchFor : INT;    
    i, j : INT;
    found : INT;                // Anzahl der Vorkommen
END_VAR

BEGIN

LenSearchIn := BYTE_TO_INT(at_SearchIn.len_act);
LenSearchFor := BYTE_TO_INT(at_SearchFor.len_act);

found := 0;
FOR i := 1 TO LenSearchIn - LenSearchFor + 1 DO
    FOR j := 1 TO LenSearchFor DO
        IF at_SearchIn.chars[i + j - 1] <> at_SearchFor.chars[j] THEN
            EXIT;
        END_IF;
    END_FOR;
    IF j > LenSearchFor THEN
        found := found + 1;
        i := i + LenSearchFor - 1;  // Suche hinter letztem Fund fortsetzen
    END_IF;
END_FOR;

StringSuchN := found;
END_FUNCTION
 

Ralle

Supermoderator
Teammitglied
Beiträge
15.103
Punkte Reaktionen
3.820
Ich hatte testweise am Schluß auch mal 'FCG', das ging, allerdings hatte ich noch Zeichen zwischen den einzelnen 'FCG'.
 

faust

Well-known member
Beiträge
571
Punkte Reaktionen
134
Ich habe dann noch folgendes ausprobiert:

Code:
STRING2 := 'FCG'
A := FIND(IN1 := STRING2,IN2 := 'FCG');

A war in diesem Fall = 0. Kann das sein?

Hallo,

gibt 'FIND' nicht die Position des ersten Vorkommens zurück? Dies wäre in deinem Fall ja 0, sprich: ganz vorne (Zeichen 0).

Ohne Gewähr, einfach aus der Hüfte geschossen ohne Verifizierung :eek:)


Gruß, Fred
 

Ralle

Supermoderator
Teammitglied
Beiträge
15.103
Punkte Reaktionen
3.820
Zuviel Werbung?
->Hier kostenlos registrieren
Hallo,

gibt 'FIND' nicht die Position des ersten Vorkommens zurück? Dies wäre in deinem Fall ja 0, sprich: ganz vorne (Zeichen 0).

Ohne Gewähr, einfach aus der Hüfte geschossen ohne Verifizierung :eek:)

Gruß, Fred

Nein, 0 wird zurückgegeben, wenn nichts gefunden wird, das 1. Zeichen hat die 1 als Pos.
 

Ralle

Supermoderator
Teammitglied
Beiträge
15.103
Punkte Reaktionen
3.820
@Ralle: Aus Interesse hatte ich gestern an der Lösung gebastelt. Zwar mit einer WHILE-Schleife aber sonst genauso mit DELETE etc...

Ich bin nicht sicher, ob ich da was falsch gemacht habe. In einem String der aus 'FCGFCGFCG' bestand, hatte ich zum Schluss noch einen STRING 'FCG' übrig, den er nicht mehr gezählt hat.

Ich habe dann noch folgendes ausprobiert:

Code:
STRING2 := 'FCG'
A := FIND(IN1 := STRING2,IN2 := 'FCG');

A war in diesem Fall = 0. Kann das sein?

Du hast Recht, wenn ich als String nur 'FCG' vorgebe, dann funktioniert Find nicht und liefert 0 zurück. Noch ein Leerzeichen dazu, dann geht es. Ich hatte zwar 'FCG' als letztes Literal im String, aber davor noch ein paar Füllzeichen. Dadurch war mein letzter String beim Suchen nicht genau 3 Zeichen lang und funktionierte.

Einziger Workarround, der mit mal eben einfällt, vor dem Bearbeiten ein beliebiges Zeichen an den String anhängen (CONCAT).

Ich hab das oben in meinem Code mal verändert. Komische Sache, das.
 
Zuletzt bearbeitet:

Tigerente1974

Well-known member
Beiträge
1.816
Punkte Reaktionen
290
Irgendwie blöd. Das gehört auch zu den Dingen die ich an solchen "fertigen" Bausteinen nicht mag. Man weiß nicht, ob der Baustein das tut was man erwartet.

Alternativ könnte man bei dem Rückgabewert von 0 mit "EQ_STRNG" auf 'FCG' vergleichen und dann ggf. am Schluß noch 1x zählen.
 

Larry Laffer

Supermoderator
Teammitglied
Beiträge
13.148
Punkte Reaktionen
2.745
Zuviel Werbung?
->Hier kostenlos registrieren
Hallo,
habt ihr mal versucht, was dabei herauskommt, wenn man statt 'FCG' einen String miz gleichem Inhalt an FIND übergibt ?
Ich könnte mir vorstellen, dass hier die übergebene "Konstante" von FIND gar nicht als wirklicher String verstanden wird - das ist jetzt aber auch nur ein "Schuß ins Blaue" ...

Gruß
Larry
 

Thomas_v2.1

Well-known member
Beiträge
8.822
Punkte Reaktionen
2.705
Hm, das scheint wohl ein Fehler in der FIND Funktion zu sein.

Dort wird vor dem Vergleich abgefragt:
1) ob die Länge von IN1 == IN2 ist, dann wird Null ausgegeben
ODER
2) ob die Länge von IN1 - IN2 < 0 ist, dann wird Null ausgegeben

1) ist so gesehen falsch. Die Kommentare die in dem Baustein vorhanden sind sagen auch etwas anderes, nämlich dass bei 1) die Länge von IN1 auf =0 geprüft werden soll.
 

Ralle

Supermoderator
Teammitglied
Beiträge
15.103
Punkte Reaktionen
3.820
@Larry
Nein, das ändert nichts, ich hab das mal mit Suchstring als Input getestet.

@Thomas_v2.1
Würde mich mal interessieren was die sich bei 1) gedacht haben, warum sollte man 2 gleichlange Strings nicht mit Find untersuchen? :confused:
 
Oben