[Erledigt] Replace unbekannter string

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

[Erledigt] Replace unbekannter string

Beitrag von MacWomble »

Hallo,

ich überlege wie ich unbekannte Platzhalter ersetzen kann. Beispiel:

Martin kauft #Farbe# #Obstsorte#

Was zwischen den Rauten steht, ist nicht bekannt und soll samt Raute ersetzt werden!
Der neue Wert wird hierbei in einen Dialog eingesetzt und abgefragt.

Bitte gib eine Farbe ein:
Bitte gib eine Obstsorte ein:

Das Problem ist, möglichst elegant den String zu parsen.

Ich könnte in einer Schleife zeichenweise durch den String gehen, jedes Zeichen prüfen um dann entsprechend bei Auffinden einer Raute zu verzweigen. Dies erscheint mir jedoch reichlich ineffektiv.
Gibt es so etwas wie 'finde erstes Vorkommen eines Zeichens' und 'finde nächstes Vorkommen' oder 'finde alle Positionen eines Zeichens'?
Zuletzt geändert von MacWomble am Sa 9. Sep 2017, 11:02, insgesamt 1-mal geändert.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6200
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Replace unbekannter string

Beitrag von af0815 »

In den Lazarussourcen wird IMHO sowas ähnliches bei den Templates gemacht.

Andreas
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
theo
Beiträge: 10468
Registriert: Mo 11. Sep 2006, 19:01

Re: Replace unbekannter string

Beitrag von theo »

MacWomble hat geschrieben:Ich könnte in einer Schleife zeichenweise durch den String gehen, jedes Zeichen prüfen um dann entsprechend bei Auffinden einer Raute zu verzweigen. Dies erscheint mir jedoch reichlich ineffektiv.


Warum? Irgend eint Tool muss das sowieso tun. Also warum nicht einfach selber durch den String gehen?
Ich würde es in diesem Fall so machen.

Wenn du es lieber kompliziert magst, kannst du auch Regular Expressions verwenden. http://wiki.freepascal.org/Regexpr/de

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2636
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Replace unbekannter string

Beitrag von m.fuchs »

MacWomble hat geschrieben:Gibt es so etwas wie 'finde erstes Vorkommen eines Zeichens' und 'finde nächstes Vorkommen' oder 'finde alle Positionen eines Zeichens'?

viewtopic.php?f=55&t=11004

Da wird beschrieben wie man Pos (bzw. PosEx) einen Parameter als Startpunkt mitgibt. Wenn du dafür den letzten Fundort verwendest kannst du nacheinander alle Doppelkreuze auffinden.
Bitte erst alle Positionen und ihre Inhalte einsammeln und anschließend ersetzen. Sonst verschiebt sich die Stringlänge und es geht schief.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Benutzeravatar
theo
Beiträge: 10468
Registriert: Mo 11. Sep 2006, 19:01

Re: Replace unbekannter string

Beitrag von theo »

Ich erkenne keinen Vorteil in der Verwendung von Pos gegenüber der einfachen, buchstabenweisen String Iteration.
Wir suchen ja Chars, keine Strings.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: Replace unbekannter string

Beitrag von MacWomble »

Danke für die vielen Hinweise !

Ich werde das wohl tatsächlich Zeichenweise durchgehen, das scheint dann ja doch der sinnvolle Weg zu sein.
Die Regular Expressions scheinen hierfür nicht geeignet zu sein, da ich den zu ersetzenden Stringteil ja nicht kenne.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: [Erledigt] Replace unbekannter string

Beitrag von wp_xyz »

Falls das eine Aufgabe für Schule/Studium ist, solltest du das auf jeden Fall mit der Zeichen-Schleife lösen, auch falls du, andererseits, schon älter bist - das schmiert das Gehirn ungemein.

In den anderen Fällen sparst du dir ein paar Zeilen Code, wenn du den String-Helper Split aufrufst - den gibt es seit FPC 3 und er trennt den String an dem als Parameter übergebenen Zeichen auf und steckt die Teilstrings in ein Array. Du kannst dann dieses StringArray Element für Element durchlaufen und für jedes zweite Element den Eingabedialog aufrufen. Du musst nur aufpassen, ob der Eingabestring gleich mit einem Platzhalter beginnt.

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2636
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Replace unbekannter string

Beitrag von m.fuchs »

theo hat geschrieben:Ich erkenne keinen Vorteil in der Verwendung von Pos gegenüber der einfachen, buchstabenweisen String Iteration.
Wir suchen ja Chars, keine Strings.

PosEx hat auch direkt eine Überladung die ein Char sucht. Klar kann man das problemlos auch alleine schreiben, denn die Implementierung von PosEx geht ja auch nur Zeichen für Zeichen durch (allerdings per IndexByte in Assembler geschrieben). Vorteil ist halt: ich schreibe keine neue Funktion dafür (die man ja auch wieder mit einer Testabdeckung versehen werden müsste), sondern benutze eine bekannte und bestehende. Ob es möglich ist vielleicht sogar schneller zu sein, in dem man eine eigene angepasste Funktion schreibt weiß ich nicht. Super-extreme Geschwindigkeit war aber auch nicht Anforderung.

MacWomble hat geschrieben:Die Regular Expressions scheinen hierfür nicht geeignet zu sein, da ich den zu ersetzenden Stringteil ja nicht kenne.

Genau dafür sind sie geeignet. Du kannst ja Platzhalter angeben.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Benutzeravatar
theo
Beiträge: 10468
Registriert: Mo 11. Sep 2006, 19:01

Re: Replace unbekannter string

Beitrag von theo »

m.fuchs hat geschrieben: Vorteil ist halt: ich schreibe keine neue Funktion dafür (die man ja auch wieder mit einer Testabdeckung versehen werden müsste), sondern benutze eine bekannte und bestehende.


Es geht natürlich beides, aber besonders lange testen muss ich für diese Zeile jetzt auch nicht...

Code: Alles auswählen

for i:=1 to length(s) do if s[i]='#' then..


.. und kann dann gleich weiter machen, mit dem Buffern des Schlüsselwortes.

P.S.: Der Trick mit Split (oder StringList.DelimitedText) ist natürlich auch nicht schlecht...

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: [Erledigt] Replace unbekannter string

Beitrag von MacWomble »

Ich habe das nun doch mit RegExpr gelöst - Das kann ja recht viel (wenn man es mal kapiert hat).

Meine Lösung, falls jemand danach sucht:

Code: Alles auswählen

function ReplacePlaceholder (AText : string;const ATag : char) : string;
var Ersatz: String;
begin
  with TRegExpr.Create do try
     Expression := '(' + ATag + '(.+?)' + ATag +')';
     if Exec (AText) then
      REPEAT
        begin
        if InputQuery('Abfragedialog', 'Bitte ' + copy(Match[1],2,length(Match[1])-2) + ' eingeben:', Ersatz) = True then
         begin
         AText:=StringReplace(AText,Match[1],Ersatz,[]);
         Ersatz:='';
         end;
        end
    UNTIL not ExecNext;
    finally Free;
   end;
  Result:=Atext;
end


Der Aufruf erfolgt z.B. so:

Code: Alles auswählen

  Textneu:=ReplacePlaceholder(dbm_description.Text,'#');
 
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

braunbär
Beiträge: 369
Registriert: Do 8. Jun 2017, 18:21
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
CPU-Target: 64Bit
Wohnort: Wien

Re: [Erledigt] Replace unbekannter string

Beitrag von braunbär »

Ich kann dir zu der Entscheidung nur gratulieren. Es ist mit großen Abstand der effizienteste Weg, etwas derartiges zu lösen. "Wenn du es lieber kompliziert magst," ist in Anbetracht des mit regex nötigen Codes im Vergleich zu jeder anderen Lösung schon ziemlicher Unfug.

Es wird übrigens noch eine Spur einfacher, wenn du die runden Klammern am Anfang und am Ende des Regex-Ausdrucks weglässt und im Abfragedialog statt

Code: Alles auswählen

copy(Match[1],2,length(Match[1])-2)
einfach nur

Code: Alles auswählen

Match[1])
eingibst. Match[1] ist dann nämlich der Teil des gefundenen Matchs, der dem '(.+?)', ohne die Tags, entspricht. Der kompette Match-String steht dir automatisch in Match[0] zur Verfügung, d. h. den kompletten regex-Ausdruck in Klammern zu setzen ist immer überflüssig.

Es gibt eine überladene Version von create, der du die Expression direkt übergeben kannst.

Und Code zwischen

Code: Alles auswählen

repeat ... until
brauchst du prinzipiell nicht in begin-end-Klammern setzen.

Wenn du sichergehen willst, dass ein beliebiges Sonderzeichen als Tag verwendet werden kann, dann solltest du statt nur der Variable Tag den String '\'+Tag verwenden. Denn es gibt ja in der Regex-Syntax eine Menge Sonderzeichen, die im String nicht für sich selbst stehen, sondern eine spezielle Bedeutung haben. Beim Tag # ist das nicht der Fall, aber wenn du als Begrenzer z.B. das Zeichen + verwenden würdest, dann würde das ganze so nicht funktionieren. Einem Sonderzeichen im Regex-String das Zeichen \ voranzustellen schadet nie und stellt sicher, dass das Zeichen vom Regex nicht "uminterpretiert" wird, auch wenn das nur bei manchen Sonderzeichen nötig ist.

Und NIE einen boolean-Ausdruck auf "=true" abfragen.

Mit diesen Änderungen schaut es dann so aus:

Code: Alles auswählen

 
function ReplacePlaceholder (const AText : string; const ATag : char) : string;
var Ersatz: String;
begin
  Result:=Atext;
  with TRegExpr.Create ('\ ' + ATag + '(.+?)\ ' + ATag) do try   // leerstellen nach \ gehören gelöscht
     if Exec (AText) then
      REPEAT
        ersatz:='';
        if InputQuery('Abfragedialog', 'Bitte ' + Match[1] + ' eingeben:', Ersatz) then
         Result:=StringReplace(Result,Match[0],Ersatz,[]);
      UNTIL not ExecNext;
    finally Free;
   end;
end
 


ACHTUNG: Anscheinend hat diese Forensoftwae einen Fehler: Im Code wird ein backslash nicht angezeigt, wenn er von einem ' gefolgt wird. Ich habe deshalb zwischen \ und ' jeweils eine Leerstelle eingefügt, aber die hat im richtigen Regex natürlich nichts verloren.
Zuletzt geändert von braunbär am So 10. Sep 2017, 13:29, insgesamt 1-mal geändert.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: [Erledigt] Replace unbekannter string

Beitrag von MacWomble »

@braunbär

Danke, das gefällt mir viel besser!

Bei ('\ ' + ATag + '(.+?)\ ' + ATag') do try ist das letzte ' zu viel!

Zum Abprüfen des boolschen Ausdrucks: Aufgrund der zuvor verwandten Programmiersprachen habe ich bisweilen solche Verwirrungen :oops:

Was RegEx angeht, werde ich mich künftig wohl mehr damit auseinandersetzen. Ist wirklich Klasse und - wenn man es mal kapiert hat - auch nicht kompliziert.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

Benutzeravatar
theo
Beiträge: 10468
Registriert: Mo 11. Sep 2006, 19:01

Re: [Erledigt] Replace unbekannter string

Beitrag von theo »

MacWomble hat geschrieben:Was RegEx angeht, werde ich mich künftig wohl mehr damit auseinandersetzen. Ist wirklich Klasse und - wenn man es mal kapiert hat - auch nicht kompliziert.


Regex kann viel, deshalb hatte ich es auch als Option erwähnt.
Kompliziert trifft es vielleicht nicht ganz, aber kryptisch und nicht intuitiv leserlich ist es schon.

Beispiel:General Email Regex (RFC 5322 Official Standard)

Code: Alles auswählen

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])


Man muss es halt mögen... :wink:

braunbär
Beiträge: 369
Registriert: Do 8. Jun 2017, 18:21
OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
CPU-Target: 64Bit
Wohnort: Wien

Re: [Erledigt] Replace unbekannter string

Beitrag von braunbär »

Naja, DAS muss man wahrscheinlich nicht mögen :wink:
In Pascal kann man ja Teilausdrücke als Konstante mit halbwegs sprechenden Namen definieren, und danach aus diesen Stringkonstanten den kompletten Regex-Ausdruck zusammenbauen - dann schaut auch der komplizierte Ausdruck schon wieder viel freundlicher aus.

Und was ist die Alternative? Ein spezialisierter Textparser, der eine Mailadresse auf valide Syntax checkt, wäre ja auch nicht unbedingt ganz trivial und in einer Minute zu durchschauen. Und wenn du das Programm gar unstrukturiert ohne vernünftige Formatierung in einer Wurst herunternudelst wie den Regex-Ausdruck, den du hier zeigst, dann verzweifelt jeder an dem Programm sicher ganz genau so wie an diesem Regex.

MacWomble hat geschrieben:Bei ('\ ' + ATag + '(.+?)\ ' + ATag') do try ist das letzte ' zu viel!

Ja, das stimmt, sorry. Habs in meinem Post ausgebessert.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: [Erledigt] Replace unbekannter string

Beitrag von MacWomble »

Die folgende Seite ist sehr hilfreich, wenn man mit RegEx arbeitet:
http://regexr.com/

und noch eine Seite mit zahlreichen vorgefertigten Ausdrücken:
https://www.freeformatter.com/regex-tester.html
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

Antworten