Komplexes Suchen/Ersetzen in einem AnsiString, geht das überhaupt?



  • Also sowas in der Art ?

    String replace(String source)
    {
       if(source.Pos("N126,0;N,24;"))
       {
          source=StringReplace(source,"N126,0;N,24;","",TReplaceFlags() << rfReplaceAll);
          source=StringReplace(source,"N,24","N,32",TReplaceFlags() << rfReplaceAll);
       }
       return source;
    }
    


  • Danke für die Antwort, aber leider nein, damit entsteht "Kollateralschaden". Es ist eben so, dass grosse Datenmenge in dem String stecken, darum muss ich die Lösung schon irgendwie mit meiner Suchmaske hinkriegen.



  • String replace(String source)
    {
       String sf="N126,0;N,24;";
       int pos=source.Pos(sf);
       int start;
       int Nc;
       while(pos>0)
       {
          start=pos;
          Nc=6;
          while(start&&Nc)
          {
             if(source[--start]=='N')
             {
                Nc--;
             }
          }
          String repl=source.SubString(start,pos-1);
          repl=StringReplace(repl,"N,24","N,32",TReplaceFlags() << rfReplaceAll);
          source.Delete(start,pos+sf.Length()-1);
          source.Insert(repl,start);
          pos=source.Pos(sf);
       }
       return source;
    }
    


  • Warum einfach, wenns auch umständlich geht.

    Reguläre Ausdrücke sind die richtige Lösung, nicht irgendein Drauflosersetzen mit StringReplace(). Die Originalvorgabe, nämlich "N??,0;N,24;N??,0;N,24;N??,0;N,24;N126,0;N,24;" durch "N??,0;N,32;N??,0;N,32;N??,0;N,32;" zu ersetzen, ist mit StringReplace() nicht korrekt umsetzbar.



  • audacia schrieb:

    Warum einfach, wenns auch umständlich geht.

    Reguläre Ausdrücke sind die richtige Lösung, nicht irgendein Drauflosersetzen mit StringReplace(). Die Originalvorgabe, nämlich "N??,0;N,24;N??,0;N,24;N??,0;N,24;N126,0;N,24;" durch "N??,0;N,32;N??,0;N,32;N??,0;N,32;" zu ersetzen, ist mit StringReplace() nicht korrekt umsetzbar.

    Refuläre Ausdrücke sind (wenn es bei diesem einfachen Fall bleibt) dafür einfach nur overloaded.
    StringReplace tut in dem Fall auch korrekt seinen Dienst.



  • Ich denke, da musst du selbst ranklotzen und dir eine entsprechende Routine basteln.

    P.s. Ich habe mal eine geschrieben, wurde über den kurzen Mittag jedoch nicht ganz fertig. Werde dir am Abend mal einen Lösungsansatz posten. 🙂



  • Hallo Zusammen

    Wie versprochen, habe ich Hier eine kleine Routine geschrieben, welche deinem Zweck entsprechen sollte.
    Dies bedeutet, dass in der Suche und im zu ersetzenden Text Wildcards verwendet werden können.

    Ich denke jedoch, dass es noch möglich sein könnte, eine kürzere Version zu basteln.
    Jedoch denke ich nicht, dass Sie mir schlecht gelungen ist.

    Mein Lösungsansatz geht nach folgendem Prinzip vor:

    • Ich lade alle Zeichen des Suchstrings (Ausser der Wildcards) in einen Map-Container
    • Im Key steht die Position im Text
    • Der Value-Parameter steht für das zeichen
    • Es wird nach dem ersten Textteil gesucht. Konnte eine Stelle gefunden werden, wird diese genauer analysiert
    • Um die Textteile zu vergleichen, können nun die Elemente im Container durchgegangen werden. Diese müssen mit der Position im Orginaltext übereinstimmen.
    • Anschliessend beginnt der zeichenbasierte Ersetzvorgang
    • Sollte der zu ersetzende Text länger als der Suchtext sein, so wird dieser übernommen. Andernfalls wird er abgeschnitten.

    Naja. Ein Code sagt mehr als 1000 erläuterungen 🙂
    Ich hoffe, ich konnte dir weiterhelfen und ein Lösungsansatz aufzeigen.

    Hier ein Beispiel:

    int _tmain(int argc, _TCHAR* argv[])
    {
    	//Rückgabewert 1: = loolenderqr,loolender4,N85ou
    	//Rückgabewert 2: = loolqr,lool,N85ou
    	wcout << WildcardFindReplace("N85pqr,N50p4,N85ou", "N??p", "loolender", '?').c_str() << endl;
    	wcout << WildcardFindReplace("N85pqr,N93p,N85ou", "N??p", "lool", '?').c_str() << endl;
    
    	//Rückgabewert 1: = N50,0;N,32;N65,0;N,32;N48,0;N,32;
    	//Rückgabewert 2: = N64,0;N,32;N12,0;N,32;N32,0;N,32;
    	wcout << WildcardFindReplace("N50,0;N,24;N65,0;N,24;N48,0;N,24;N126,0;N,24;","N??,0;N,24;N??,0;N,24;N??,0;N,24;N126,0;N,24;", "N??,0;N,32;N??,0;N,32;N??,0;N,32;", '?').c_str() << endl;
    	wcout << WildcardFindReplace("N64,0;N,24;N12,0;N,24;N32,0;N,24;N126,0;N,24;","N??,0;N,24;N??,0;N,24;N??,0;N,24;N126,0;N,24;", "N??,0;N,32;N??,0;N,32;N??,0;N,32;", '?').c_str() << endl;
    
    	//Rückgabewert 1: = N64,0;N,24;N12,0;N,24;N32,0;P,24;N126,0;N,24;
    	wcout << WildcardFindReplace("N64,0;N,24;N12,0;N,24;N32,0;P,24;N126,0;N,24;","N??,0;N,24;N??,0;N,24;N??,0;N,24;N126,0;N,24;", "N??,0;N,32;N??,0;N,32;N??,0;N,32;", '?').c_str() << endl;
    
    	//Rückgabewert 1: = hakko
    	wcout << WildcardFindReplace("hallo","l","k",NULL).c_str() << endl;;
    
    	std::cin.get();
    	return 0;
    }
    // ---------------------------------------------------------------------------
    
    UnicodeString __fastcall TF_Hauptformular::WildcardFindReplace(UnicodeString sText, UnicodeString sFind, UnicodeString sReplace, wchar_t cWildcard)
    {
        UnicodeString sReturnString = "";
        UnicodeString sSearchBegin = "";
    	UnicodeString sStringPart="";
    
        int iFoundStart = 0;
        int iTextLength = sText.Length();
        int iFindLength = sFind.Length();
    	int iReplaceLength = sReplace.Length();
    	int iWildcardNumber = 0;
    	int iFindWildcardAnz = 0;
    	bool bCompareSuccessfull;
    
    	std::map<int, wchar_t> mapRelevantCharacters;
    	std::map<int, wchar_t>::iterator iteratorRelevantChars;
    
    	std::map<int, wchar_t> mapWildcardCharacters;
    	std::map<int, wchar_t>::iterator iteratorWildcardChars;
    
        //Wurden Wildcards <?> angegeben, wenn nicht, dann wird die standardfunktion verwendet
    	if (cWildcard == NULL || sFind.Pos(cWildcard) <= 0)
    		sReturnString = StringReplace(sText, sFind, sReplace, TReplaceFlags() << rfReplaceAll);
        else
        {
            //Ermitteln der zu berücksichtigenden Zeichen (Ohne Wildcards)
            mapRelevantCharacters.clear();
            sSearchBegin = sFind.SubString(1, sFind.Pos("?") - 1);
    
    		for (int i = 1; i <= sFind.Length(); i++)
    		{
    			if (sFind[i] != cWildcard)
    			{
    				mapRelevantCharacters.insert(std::pair<const int, wchar_t>(i - 1, sFind[i]));
    			}
    		}
    
    		for (int i = 1; i <= sFind.Length(); i++)
    		{
    			if (sFind[i] == cWildcard)
    			{
    				++iFindWildcardAnz;
    				mapWildcardCharacters.insert(std::pair<const int, wchar_t>(i - 1, sFind[i]));
    			}
    		}
    
    		//Prüfen, ob der Suchstring vorkommt
    		if (sSearchBegin == EmptyStr)
    			iFoundStart = 1;
    		else
    			iFoundStart = sText.Pos(sSearchBegin);
            while (iFoundStart > 0)
            {
                // Prüfen, ob die nachfolgende Zeichenfolge übereinstimt
                bCompareSuccessfull = true;
                iteratorRelevantChars = mapRelevantCharacters.begin();
                while (iteratorRelevantChars != mapRelevantCharacters.end())
                {
                    //Die Relevanten Zeichen werden an der erwarteten Position gesucht.
    				//Ist es nicht vorhanden, muss der Rest nicht mehr geprüft werden
    				if (sText[iFoundStart + iteratorRelevantChars->first] != iteratorRelevantChars->second) {
                        bCompareSuccessfull = false;
                        iteratorRelevantChars=mapRelevantCharacters.end();
                    }
    				else
    					++iteratorRelevantChars;
                }
    
                //Stimmte der Text erfolgreich überein, so kann der Ersetzvorgang durchgeführt werden
                //Ansonsten werden die Zeichen bis zum nächsten Fund übernommen.
                if (bCompareSuccessfull) {
                    //Text vor Fund hinzufügen (Wenn es sich nicht um das erste Zeichen des gefundenen Textes handelt)
    				if (iFoundStart > 1)
    					sReturnString += sText.SubString(1,iFoundStart-1);
    
                    //Den zu ersetzenden Teilstring laden und aus dem Eingangstring löschen
                    sStringPart = sText.SubString(iFoundStart,iFindLength);
                    sText.Delete(1,iFoundStart + iFindLength -1);
    
                    //Hier werden so viele Zeichen gegenüber gestellt, wie sie im Such und Ersetz-String identisch sind.
    				//Der Rest wird dann abeschnitten oder hinzugefügt
    				iWildcardNumber=0;
    				for (int i = 1; i <= iReplaceLength; i++) {
                        //Ist im Replace-Text ein Wildcard vorhanden, wird das Zeichen vom normalen Text übernommen.
    					if (sReplace[i]==cWildcard) {
    						iteratorWildcardChars = mapWildcardCharacters.begin();
    						for (int k = 1; k <= iWildcardNumber && iteratorWildcardChars != mapWildcardCharacters.end(); k++) {
    							++iteratorWildcardChars;
    						}
    						if (iteratorWildcardChars != mapWildcardCharacters.end()) {
    							sReturnString += sStringPart[iteratorWildcardChars->first + 1];
    						}
    
    						//Dadurch werden die Wildcards zzrückgesetzt und Symbole Zeichen können ein weiteres mal gesetzt werden
    						iWildcardNumber = (iWildcardNumber == iFindWildcardAnz - 1) ? 0 : iWildcardNumber + 1;
    					}
    					else
    						sReturnString += sReplace[i];
                    }
                }
                else{
                    sReturnString += sText.SubString(1,iFoundStart);
                    sText.Delete(1,iFoundStart);
                }
    
                //Text bis zum nächsten Zeichen Finden.
                iFoundStart = sText.Pos(sSearchBegin);
    
                //Wurde nichts mehr gefunden, muss der restliche Text noch zum Ausgabestring
                //hinzugefügt werden.
    			if (iFoundStart == 0)
    				sReturnString += sText;
            }
        }
    
    	return sReturnString;
    }
    // ---------------------------------------------------------------------------
    

    mfg
    Deforation alias (unregistered) Kreilon



  • Das ist jetzt ein Scherz, oder?

    - UnicodeString::t_str() benutzt man nicht. Wirklich nicht. Nie. Die Funktion korrumpiert unter Umständen Daten und wird folgerichtig mit großer Sicherheit in der nächsten C++Builder-Version wieder verschwinden.

    - Du hast mein "warum einfach, wenns auch umständlich geht" vermutlich etwas zu ernst genommen 🤡

    So gehts richtig:

    String performReplace (String input)
    {
        std::auto_ptr <TPerlRegEx> pre (new TPerlRegEx (0));
        pre->RegEx = "N(.{2}),0;N,24;N(.{2}),0;N,24;N(.{2}),0;N,24;N126,0;N,24;";
        pre->Subject = input;
        pre->Replacement = "N\\1,0;N,32;N\\2,0;N,32;N\\3,0;N,32;";
        pre->ReplaceAll ();
        return pre->Subject;
    }
    

    Wenn die Schnittstelle von TPerlRegEx nicht so umständlich wäre, wäre es sogar ein Einzeiler.

    chronol schrieb:

    Refuläre Ausdrücke sind (wenn es bei diesem einfachen Fall bleibt) dafür einfach nur overloaded.

    Was ist daran jetzt "overloaded" 😕

    Edit: s/w_str/t_str



  • chronol schrieb:
    Refuläre Ausdrücke sind (wenn es bei diesem einfachen Fall bleibt) dafür einfach nur overloaded.
    Was ist daran jetzt "overloaded"

    Ganz einfach.Du wirst auch wenn du noch so lange suchst im C++ Builder 4 nie eine Klasse TPerlRegEx finden.



  • Und?



  • Und dann braucht man nicht mit Kanonen auf ....



  • Habe gerade entdeckt, dass man ja im Thread eine Seite weiterschalten kann. 🙂

    Wow, so viele Beiträge. Danke Leute!

    Ich melde mich bald nach hoffentlich geglückten Tests wieder.

    Viele Grüsse



  • Hallo alle!

    Nochmals herzlichen Dank an alle für eure Mühe!

    Auch wenns mir etwas peinlich ist, aber ich habe es noch nicht hingekriegt, bin aber noch weiter am experimentieren.

    @chronol
    Leider sind die Daten nach Anwendung der Funktion nicht mehr konsistent. 😞

    @audacia
    Die Kurzfassung mittels regulären Audrücken scheitert tatsächlich daran, dass TPerlRegEx nicht unter BCB4 funktioniert, zumindest wüsste ich nicht wie sich die Komponente installieren lässt, wobei meine Version 4 ja auch schon steinzeitlich ist. Anscheinend basiert die Komponente auf Perl, könnte man dann vielleicht den Ersetzungsvorgang irgendwie "outsourcen" an ein entsprechendes Perl-Skript das mit regulären Audrücken umgehen kann?

    Habe mich mal nach aktuellen C++ Builder Versionen erkundigt und hatte einen Preisschock, so was leistet man sich doch nicht als Student und Gelegenheitsprogrammierer, warum gibts da keine Einsteigerversion mit zumutbarem Preis und beschränktem Funktionsumfang für die nichtkommerzielle Nutzung? Ok, das ist jetzt etwas offtopic.

    @Deforation
    Die lange ausführliche Lösung (danke vielmals für die Arbeit!!) konnte ich nicht kompilieren, weil der Compiler eine ganze Liste von Fehlern meldet, die aber wohl einfach damit zusammenhängen, dass ein Teil der Funktionen (und Bibliotheken?) in meiner alten BCB Version nicht existieren. Ich versuche nun entsprechende Stellen umzuschreiben, bzw. die Logik zu übersetzen, wobei ich wirklich Anfänger bin und ich die Syntax und Funktionen teilweise gar nicht verstehe. (Trotz vorbildlicher Doku.) Bleibe aber an der Sache dran.

    Was soll an w_str() nicht in Ordnung sein, das habe ich nicht mitbekommen?

    Viele Grüsse



  • lichtmagie schrieb:

    @chronol
    Leider sind die Daten nach Anwendung der Funktion nicht mehr konsistent. 😞

    StringReplace ist, wie ich sagte, hierfür leider ungeeignet.

    lichtmagie schrieb:

    @audacia
    Die Kurzfassung mittels regulären Audrücken scheitert tatsächlich daran, dass TPerlRegEx nicht unter BCB4 funktioniert, zumindest wüsste ich nicht wie sich die Komponente installieren lässt, wobei meine Version 4 ja auch schon steinzeitlich ist.

    Das kann man so sagen. Es ist ohnehin höchste Zeit für ein Upgrade, wenn du mich fragst.

    lichtmagie schrieb:

    Anscheinend basiert die Komponente auf Perl, könnte man dann vielleicht den Ersetzungsvorgang irgendwie "outsourcen" an ein entsprechendes Perl-Skript das mit regulären Audrücken umgehen kann?

    Die Komponente heißt so, weil sie PCRE (Perl Compatible Regular Expressions) benutzt. Doku dazu hier.

    C++Builder 4 habe ich nicht, aber beim C++Builder 6 ist PCRE mitgeliefert. Schau einfach mal nach, ob pcre.h bei dir im Include-Verzeichnis liegt. Falls nicht, lade eben PCRE herunter.

    lichtmagie schrieb:

    als Student und Gelegenheitsprogrammierer

    ... kann man die SSL-Versionen zu moderateren Preisen beziehen.

    lichtmagie schrieb:

    warum gibts da keine Einsteigerversion mit zumutbarem Preis und beschränktem Funktionsumfang für die nichtkommerzielle Nutzung?

    Das gab es in der Vergangenheit mehrfach, und es wird gerade auch in den Delphi-Newsgroups diskutiert. Hier ein offizielles Statement (der Rest des Threads ist auch lesenswert).

    lichtmagie schrieb:

    Was soll an w_str() nicht in Ordnung sein, das habe ich nicht mitbekommen?

    http://edn.embarcadero.com/article/38475#13UnicodeStringtstrNarrowsWideData
    https://forums.codegear.com/thread.jspa?threadID=6426



  • Entgegen audacias Meinung ist StringReplace durchaus geeignet.
    In deinem Fall müsstest du halt sehen, wo es genau hängt.
    Ohne genaue Kenntniss des Aufbaus deines Strings ist das kaum möglich.



  • chronol schrieb:

    Entgegen audacias Meinung ist StringReplace durchaus geeignet.

    Ich habe noch keine StringReplace()-basierte Lösung gesehen, die GENAU das Pattern "N??,0;N,24;N??,0;N,24;N??,0;N,24;N126,0;N,24;" durch "N??,0;N,32;N??,0;N,32;N??,0;N,32;" ersetzt. (Die einzige neben der Regex-basierten Lösung als potentiell brauchbar einzustufende ist die von Deforation, der anscheinend gerne das Rad neu erfindet 😉 und außerdem auf StringReplace() verzichtet).

    Wie dem auch sei, hier ist eine kleine triviale Wrapperklasse für PCRE, die die Grundfunktionen (Match und Replace) erfüllt. Damit reduziert sich die Komplexität der Regex-Lösung auf zwei Zeilen:

    String performReplace (String input)
    {
        RegEx re ("N(.{2}),0;N,24;N(.{2}),0;N,24;N(.{2}),0;N,24;N126,0;N,24;");
        return re.replaceAll (input, "N\\1,0;N,32;N\\2,0;N,32;N\\3,0;N,32;");
    }
    

    Funktioniert wie gewünscht, ganz ohne "genaue Kenntnis des Aufbaus deines Strings".

    Jetzt bitte dasselbe einmal mit StringReplace().



  • Es scheint, als wäre die Lösung so nah wie nie zu vor! 🙂

    Aber ich erhalte noch folgende Fehlermeldung:

    [Linker Fehler] Unresolved external '_pcre_free' referenced from C:\PROGRAMME\BORLAND\EIGENE PROJEKTE\PROJEKT.OBJ.

    Die "pcre.h" war schon vorhanden, also habe ich diese inkludiert plus zusätzlich habe ich regex.hpp und regex.cpp heruntergeladen und ebenfalls inkludiert.

    Doch im Zusammenhang mit dem Aufruf der Funktion performReplace() kommt dann die obige Fehlermeldung...



  • Ich habe noch keine StringReplace()-basierte Lösung gesehen, die GENAU das Pattern "N??,0;N,24;N??,0;N,24;N??,0;N,24;N126,0;N,24;" durch "N??,0;N,32;N??,0;N,32;N??,0;N,32;" ersetzt.
    

    Dann müsstest du halt etwas zurückblättern ... 😉



  • Jetzt bitte dasselbe einmal mit StringReplace().

    Die leicht bereinigte Version...

    String replace(String source)
    {
       String sf="N126,0;N,24;";
       int pos=source.Pos(sf);
       int start;
       int Nc;
       while(pos>0)
       {
          start=pos;
          Nc=6;
          while(start&&Nc)
          {
             if(source[--start]=='N')
             {
                Nc--;
             }
          }
          String repl=source.SubString(start,pos-start);
          repl=StringReplace(repl,"N,24","N,32",TReplaceFlags() << rfReplaceAll);
          source.Delete(start,pos-start+sf.Length());
          source.Insert(repl,start);
          pos=source.Pos(sf);
       }
       return source;
    }
    


  • chronol schrieb:

    Die leicht bereinigte Version...

    ... erfüllt nicht ihren Zweck.

    Dein Konstrukt ersetzt beispielsweise auch "N??,0;N,128;N??,0;N,24;N??,0;N,24;N,31;N126,0;N,24;" durch "N??,0;N,128;N??,0;N,32;N??,0;N,32;N,31;", was nicht erwünscht ist. Und wenn ich es mit "N126,0;N,24;N126,0;N,24;" füttere, fliegt eine Exception.

    Es ist schlicht fahrlässig, für eine derart präzise gestellte Aufgabe mit Bruteforce-Techniken wie StringReplace() heranzugehen, ganz abgesehen davon natürlich, daß es offensichtlich überaus fehleranfällig ist. Reguläre Ausdrücke sind die richtige Lösung.

    lichtmagie schrieb:

    Aber ich erhalte noch folgende Fehlermeldung:

    [Linker Fehler] Unresolved external '_pcre_free' referenced from C:\PROGRAMME\BORLAND\EIGENE PROJEKTE\PROJEKT.OBJ.

    Linkst du gegen die dynamische RTL? Falls ja, sieh mal nach (mit Dependency Walker), ob die DLL das Symbol exportiert.

    Wenn du es nicht hinbekommst, kannst du auch einfach std::free() stattdessen verwenden; die PCRE-Allokationsfunktionen leiten Aufrufe standardmäßig an std::malloc() und std::free() weiter. Allerdings solltest du in regex.cpp dann in einem Kommentar auf den Workaround hinweisen.


Anmelden zum Antworten