[Gelöst] Substring in langem String performant ersetzen

16. August 2021 10:13

Wir haben diese Funktion hier im System zum "Suchen & Ersetzen" innerhalb eines Strings:

https://forum.mibuso.com/discussion/com ... ent_323963

Die funktioniert auch, ist aber bei einem 42MB-String total unperformant; es dauert damit knapp 2 Stunden, bis 51000 Vorkommen eines Strings ersetzt wurden. In Notepad++ dauert das wenige Sekunden.

Ich hab gedacht, dass ich evtl. auch .NET zurückgreifen kann und System.String nehme. Darf man aber wohl leider nicht in NAV. Auch der Workaround, System.Text.StringBuilder zu verwenden, geht in NAV 2016 scheinbar nicht mehr:
https://navfreak.com/2015/09/21/dynamic ... em-string/

Hat jemand noch eine Idee?

Lösung: wenn man eine .NET-Klasse nicht direkt bei der Variablenanlage in NAV findet, kann man nach der Klasse googeln, z.B. "System.String". Dann steht hinter Assembly der Wert, den man zuerst auswählen kann und dann erscheint auch die gewünschte Klasse darunter zur Auswahl. Hier z.B. dann eine .NET-Variable anlegen und mscorlib auswählen und dann kann man auch System.String auswählen und dann ist das Suchen und Ersetzen ein Zweizeiler (SystemString ist wie gerade beschrieben eine .NET - Variable):

ReplaceSubstringInStringDotNet(String : Text;SearchString : Text;ReplaceString : Text) : Text
SystemString := String;
EXIT(SystemString.Replace(SearchString, ReplaceString));


Offtopic:
Das ist doch wieder unfassbar, dass Microsoft keine String.Replace-Funktion in NAV zu Verfügung stellt und die, die man sich basteln kann, ist unperformant und eigentlich kann man ja .NET verwenden, aber System.String nicht. Den ganzen $%§# muss ich auch nur machen, weil XmlPorts nicht mit Attributen umgehen können, die einen Doppelpunkt enthalten: viewtopic.php?f=68&t=37558#p145323
Ich könnte natürlich jetzt auch einen XmlPort mit 300 Feldern wegschmeißen und das ganze nochmal per .NET XmlDocument programmieren, juhu...
Zuletzt geändert von InfoWissler am 17. August 2021 09:46, insgesamt 2-mal geändert.

Re: Substring in langem String performant ersetzen

16. August 2021 10:42

Hallo,

was möchtest du denn ersetzen? Einen Buchstaben, ein Wort, ein Wort mit unterschiedlicher Länge?


Gruß Fiddi

Re: Substring in langem String performant ersetzen

16. August 2021 11:09

InfoWissler hat geschrieben:Ich hab gedacht, dass ich evtl. auch .NET zurückgreifen kann und System.String nehme. Darf man aber wohl leider nicht in NAV. Auch der Workaround, System.Text.StringBuilder zu verwenden, geht in NAV 2016 scheinbar nicht mehr:
https://navfreak.com/2015/09/21/dynamic ... em-string/

Was geht daran nicht in NAV 2016? In dem Artikel geht es um die Einschränkungen, die NAV 2009 R2 im Vergleich zu den späteren NAV-Versionen hatte. Das war ja die erste Version mit der Möglichkeit, .NET einzubinden.

Selbst wenn es intern nicht gehen sollte, kann PowerShell praktisch alles nutzen, was .NET bietet, und Skripte können problemlos aus NAV gestartet werden.
Entweder mit dem PowerShellRunner
https://cloudblogs.microsoft.com/dynami ... er-add-in/
oder so für völlige Freiheit beim Erstellen :wink: .

Re: Substring in langem String performant ersetzen

16. August 2021 11:18

Ich versuche, xml:lang durch xmllang zu ersetzen.

Ich wollte es gerade nochmal mit System.Text.RegularExpressions versuchen.

PowerShell ist auch eine gute Idee, versuche ich auch einmal, danke! :)

Re: Substring in langem String performant ersetzen

16. August 2021 11:53

Hallo,

wenn du xml:lang auch durch xml_lang ersetzen könntest, gäbe es u.U. folgende - evtl. schnellere - Lösung (Meta Code)
Code:
procedure ReplaceDP(inpstring:text) outring:text
VAR
 i:integer;
BEGIN
  outstring = inpstring;
  for i := 1 to strlen(inpstring) do
     if inpstring[i] ='x' then
       if (inpstring[i+1] ='m') and (inpstring[i+3] = ':') and (inpstring[i+4] = 'l') and.... // if -Abfrage optimieren
         then
            outpstring[i+3] ='_';
END;


Gruß Fiddi

Re: Substring in langem String performant ersetzen

16. August 2021 15:57

Ich wollte es jetzt per PowerShell probieren, aber leider bin ich da noch zu blöd zu:

Ich wollte die PowerShellvariable erst wie hier anlegen, allerdings kann ich die .NET-Variable Microsoft.Dynamics.Nav.PowerShellRunner nicht auswählen: https://cloudblogs.microsoft.com/dynami ... er-add-in/
Wir haben NAV 2016, allerdings noch ein sehr frühes CU, kann das daran liegen, dass das da noch nicht drin war?

Naja, dann habe ich angefangen, es über die .NET-Komponente 'Windows Script Host Object Model'.WshShell (Variablenname: Shell) zu versuchen:

LOCAL ReplaceSubstringInString(String : Text;SearchString : Text;ReplaceString : Text) ReturnText : Text
CREATE(Shell,TRUE,TRUE);
ReturnText := FORMAT(Shell.Run(STRSUBSTNO(ShellCommand, String, SearchString)));

Inhalt der Textkonstante ShellCommand: powershell -Command "'%1' -replace '%2', '%3'"

Allerdings ist der Rückgabewert ein Integer (deswegen auch das FORMAT beim testen) und nicht der String. Ich habe gerade keine Idee, wie ich den bearbeiteten String von der Kommandozeile zurück in NAV bekomme, hat da jemand einen Tipp? Ich könnte jetzt anfangen, Dateien zu erstellen, hoffe aber, dass ich darum herumkomme?


Edit: ich bin gerade auch nochmal über das Thema gestolpert, dass bei der WSH-Lösung dann ja eine Nachricht kommen würde, dass man die DLL xy ausführen wird und ob man das denn zulassen möchte. Hatte ich schonmal, war aber kein Problem, weil das nur Benutzer ausführen und die klicken dann halt beim ersten Mal auf Immer zulassen. Muss man dann bei einer Ausführung per Job tricksen so wie hier? https://forum.mibuso.com/discussion/308 ... shell-exec
Zuletzt geändert von InfoWissler am 16. August 2021 16:49, insgesamt 2-mal geändert.

Re: Substring in langem String performant ersetzen

16. August 2021 16:24

Hallo,

doch, ein String ist verwendbar wie ein Array vom Typ Char.
Daher sollte das mit dem Index funktionieren.

wie vollständig du die zweite if- Abfrage machst, hängt davon ab, welche ähnlichen Texte in deinem Text vorkommen.

evtl. ist es auch schneller folgende if-Anweisung zu benutzen:
Code:
inpstring +='        '; // Leerzeichen in 'xml:lang'- Länge, damit die Suche am Stringende nicht auf die Nase fällt
len = strlen(inpstring);
i = 0
while (i <len) do begin
  i+=1;
  if inpstring[i] = 'x' then
    if inpstring[i+1] = 'm' then
      if inpstring[i+2] = 'l' then
       ..
       BEGIN
        outstring[i+3] = '_';
        i+=8;
      END;
END;
;
Denn C/AL kennt kein expression - shortcut. D.h. bei einem IF- Vergleich werden immer alle Bedingungen geprüft, nicht nur die bis zur ersten nicht zutreffenden.

Gruß Fiddi

Re: Substring in langem String performant ersetzen

16. August 2021 16:50

Sorry, ich hab in meinem obigen Post zu viel editiert und dann nochmal neue Ideen gehabt und probiert. Mein aktueller Stand zur Funktion ist folgender:


Das Ersetzen vom Doppelpunkt mit dem Unterstrich funktioniert, allerdings möchte ich den Doppelpunkt komplett entfernen und sowohl := '' als auch Clear funktionieren leider nicht. HIer die Funktion in NAV:
LOCAL ReplaceXmlLangInString(inpstring : Text) outpstring : Text
outpstring := inpstring;
FOR i := 1 TO STRLEN(inpstring) DO BEGIN
IF inpstring[i] = 'x' THEN BEGIN
IF (inpstring[i+1] ='m') AND (inpstring[i+2] = 'l') AND (inpstring[i+3] = ':') AND (inpstring[i+4] = 'l') AND (inpstring[i+5] = 'a')
AND (inpstring[i+6] = 'n') AND (inpstring[i+7] = 'g')
THEN BEGIN
outpstring[i+4] := '_';
END;
END;
END;

Update: ein Kollege hatte jetzt die Lösung für das ursprüngliche Problem, dass die System.String- Klasse nicht auswählbar war: ich musste einfach zuerst mscorlib auswählen und dann kann man auch System.String auswählen.
Zuletzt geändert von InfoWissler am 16. August 2021 17:07, insgesamt 1-mal geändert.

Re: Substring in langem String performant ersetzen

16. August 2021 16:58

Hallo,

das Ersetzen des ':' kann mit deiner Funktion eigentlich nicht funktionieren, denn der sollte bei i+3 stehen.

Um den ':' komplett zu ersetzen, solltest du den ':' nicht durch einen '_' ersetzen, sondern durch ein Zeichen, das im Text nicht vorkommt.

dann kannst du am Ende mit DELCHAR( outstring,'=','GANZ_BESONDRES_ZEICHEN'); auch das Zeichen aus dem Text entfernen.

Gruß fiddi

Re: Substring in langem String performant ersetzen

16. August 2021 17:28

InfoWissler hat geschrieben:Wir haben NAV 2016, allerdings noch ein sehr frühes CU, kann das daran liegen, dass das da noch nicht drin war?

Im Ordner der ServiceTier, also auf der Maschine wo der Dienst läuft, im Pfad
C:\Program Files\Microsoft Dynamics NAV\90\Service\Add-ins\PowerShellRunner\ sollte die Datei Microsoft.Dynamics.Nav.PowerShellRunner.dll liegen, in allen CUs und der RTM-Version.

Re: Substring in langem String performant ersetzen

17. August 2021 09:45

Lösung für das ursprüngliche Problem:


Lösung: wenn man eine .NET-Klasse nicht direkt bei der Variablenanlage in NAV findet, kann man nach der Klasse googeln, z.B. "System.String". Dann steht hinter Assembly der Wert, den man zuerst auswählen kann und dann erscheint auch die gewünschte Klasse darunter zur Auswahl. Hier z.B. dann eine .NET-Variable anlegen und mscorlib auswählen und dann kann man auch System.String auswählen und dann ist das Suchen und Ersetzen ein Zweizeiler (SystemString ist wie gerade beschrieben eine .NET - Variable):

ReplaceSubstringInStringDotNet(String : Text;SearchString : Text;ReplaceString : Text) : Text
SystemString := String;
EXIT(SystemString.Replace(SearchString, ReplaceString));

Hab ich auch in den Ausgangspost editiert.

Re: [Gelöst] Substring in langem String performant ersetzen

17. August 2021 09:56

Hallo,

aber denk dran: Solltest du mal in die Cloud migrieren, hast du kein DotNet zur Verfügung.

Gruß fiddi

Re: [Gelöst] Substring in langem String performant ersetzen

17. August 2021 10:21

Falls man das System irgendwann in die Cloud verlegen möchte, wäre die Azure Data Factory eine Möglichkeit.
https://docs.microsoft.com/en-us/azure/data-factory/data-flow-expression-functions#replace
Kann man auch in NAV 2016 schon nutzen, ist aber mit Kosten verbunden :wink: .

Re: [Gelöst] Substring in langem String performant ersetzen

17. August 2021 19:36

fiddi hat geschrieben:Solltest du mal in die Cloud migrieren, hast du kein DotNet zur Verfügung.

Dort kann man dann aber die Standardimplementierung nutzen: https://docs.microsoft.com/en-us/dynami ... ace-method