[Erledigt] Replace unbekannter string

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut

[Erledigt] Replace unbekannter string

Beitragvon MacWomble » 9. Sep 2017, 07:46 [Erledigt] Replace unbekannter string

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 9. Sep 2017, 10:02, insgesamt 1-mal geändert.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.
MacWomble
 
Beiträge: 363
Registriert: 17. Apr 2008, 00:59
Wohnort: Freiburg
OS, Lazarus, FPC: Mint 18.1 Cinnamon / CodeTyphon Generation V Plan 6.00 (FPC 3.1.1) | 
CPU-Target: 32/64 Nit
Nach oben

Beitragvon af0815 » 9. Sep 2017, 08:00 Re: Replace unbekannter string

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).
af0815
 
Beiträge: 3257
Registriert: 7. Jan 2007, 10:20
Wohnort: Niederösterreich
OS, Lazarus, FPC: Win7/Linux (L stable FPC stable) per fpcup | 
CPU-Target: 32Bit (64Bit)
Nach oben

Beitragvon theo » 9. Sep 2017, 08:47 Re: Replace unbekannter string

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
theo
 
Beiträge: 7879
Registriert: 11. Sep 2006, 18:01

Beitragvon m.fuchs » 9. Sep 2017, 08:59 Re: Replace unbekannter string

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
m.fuchs
 
Beiträge: 1670
Registriert: 22. Sep 2006, 18:32
Wohnort: Berlin
OS, Lazarus, FPC: Winux (L 1.6, FPC 3.0) | 
CPU-Target: x86, x64, arm
Nach oben

Beitragvon theo » 9. Sep 2017, 09:28 Re: Replace unbekannter string

Ich erkenne keinen Vorteil in der Verwendung von Pos gegenüber der einfachen, buchstabenweisen String Iteration.
Wir suchen ja Chars, keine Strings.
theo
 
Beiträge: 7879
Registriert: 11. Sep 2006, 18:01

Beitragvon MacWomble » 9. Sep 2017, 09:59 Re: Replace unbekannter string

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.
MacWomble
 
Beiträge: 363
Registriert: 17. Apr 2008, 00:59
Wohnort: Freiburg
OS, Lazarus, FPC: Mint 18.1 Cinnamon / CodeTyphon Generation V Plan 6.00 (FPC 3.1.1) | 
CPU-Target: 32/64 Nit
Nach oben

Beitragvon wp_xyz » 9. Sep 2017, 10:50 Re: [Erledigt] Replace unbekannter string

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.
wp_xyz
 
Beiträge: 2249
Registriert: 8. Apr 2011, 08:01

Beitragvon m.fuchs » 9. Sep 2017, 10:54 Re: Replace unbekannter string

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
m.fuchs
 
Beiträge: 1670
Registriert: 22. Sep 2006, 18:32
Wohnort: Berlin
OS, Lazarus, FPC: Winux (L 1.6, FPC 3.0) | 
CPU-Target: x86, x64, arm
Nach oben

Beitragvon theo » 9. Sep 2017, 13:47 Re: Replace unbekannter string

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...
theo
 
Beiträge: 7879
Registriert: 11. Sep 2006, 18:01

Beitragvon MacWomble » 9. Sep 2017, 14:23 Re: [Erledigt] Replace unbekannter string

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.
MacWomble
 
Beiträge: 363
Registriert: 17. Apr 2008, 00:59
Wohnort: Freiburg
OS, Lazarus, FPC: Mint 18.1 Cinnamon / CodeTyphon Generation V Plan 6.00 (FPC 3.1.1) | 
CPU-Target: 32/64 Nit
Nach oben

Beitragvon braunbär » 9. Sep 2017, 18:48 Re: [Erledigt] Replace unbekannter string

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 10. Sep 2017, 12:29, insgesamt 1-mal geändert.
braunbär
 
Beiträge: 164
Registriert: 8. Jun 2017, 17:21

Beitragvon MacWomble » 9. Sep 2017, 22:39 Re: [Erledigt] Replace unbekannter string

@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.
MacWomble
 
Beiträge: 363
Registriert: 17. Apr 2008, 00:59
Wohnort: Freiburg
OS, Lazarus, FPC: Mint 18.1 Cinnamon / CodeTyphon Generation V Plan 6.00 (FPC 3.1.1) | 
CPU-Target: 32/64 Nit
Nach oben

Beitragvon theo » 10. Sep 2017, 08:25 Re: [Erledigt] Replace unbekannter string

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:
theo
 
Beiträge: 7879
Registriert: 11. Sep 2006, 18:01

Beitragvon braunbär » 10. Sep 2017, 11:56 Re: [Erledigt] Replace unbekannter string

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.
braunbär
 
Beiträge: 164
Registriert: 8. Jun 2017, 17:21

Beitragvon MacWomble » 10. Sep 2017, 13:22 Re: [Erledigt] Replace unbekannter string

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.
MacWomble
 
Beiträge: 363
Registriert: 17. Apr 2008, 00:59
Wohnort: Freiburg
OS, Lazarus, FPC: Mint 18.1 Cinnamon / CodeTyphon Generation V Plan 6.00 (FPC 3.1.1) | 
CPU-Target: 32/64 Nit
Nach oben

» Weitere Beiträge siehe nächste Seite »
Nächste

Zurück zu Freepascal



Wer ist online?

Mitglieder in diesem Forum: Google [Bot] und 2 Gäste

porpoises-institution
accuracy-worried