Komplexes Suchen/Ersetzen in einem AnsiString, geht das überhaupt?
-
Hallo Leute, bin neu hier...
Vorweg, bin noch ein ziemlich schlechter Programmierer (durchschaue z.B. das String-Handling in C noch nicht), "wurstle" mit AnsiStrings herum und scheitere an einer (scheinbar?) unlösbaren Aufgabe! Ich fürchte wohl mein Vorhaben ist kaum mit einer Ansi-String Standardroutine zu erledigen, oder doch?
Ich sollte eine grosse Menge Daten folgendermassen umformen:
suchen nach: (Fragezeichen sind variable Ziffern)
"N??,0;N,24;N??,0;N,24;N??,0;N,24;N126,0;N,24;"
ersetzen durch:
"N??,0;N,32;N??,0;N,32;N??,0;N,32;"(Der Schluss N126... wird gelöscht.)
Die variablen Ziffern (??) sollen dabei natürlich beibehalten werden.
Ich habe zwar die Funktion StringReplace entdeckt, aber die taugt für meinen Fall nicht, weil das Ersetzen nicht im Zusammenhang mit Platzhaltern funktioniert.
Wie realisiert man komplexes Suchen/Ersetzen-Operationen unter Berücksichtigung von Platzhaltern?
Für Hilfe wäre ich sehr dankbar, brauche eine Lösung ziemlich dringend.
Danke euch und viele Grüsse
-
Ergänzung: verwende den alten BCB4
-
Reicht es denn nicht, wenn du nach "N,24;" suchst und dies dann durch "N,32;" ersetzt?
Und mit
AnsiString sText; int nIndex = sText.Pos("N126"); sText.Delete(nIndex, sText.Length());
suchst du den Eintrag "N126" und behältst nur alles, was vor diesem Text ist.
-
Ich habe zwar die Funktion StringReplace entdeckt, aber die taugt für meinen Fall nicht, weil das Ersetzen nicht im Zusammenhang mit Platzhaltern funktioniert.
Das kann ich nicht nachvollziehen. Du musst doch irgendwann wissen, was du genau ersetzen willst.
Wenn du z.B. jede Zahl nach einem 'N' ersetzen willst, dann suchst du halt unter Verwendung von AnsiString::Pos nach der Position im String und holst dir dann die Zahl nach dem 'N' (SubStr).
Oder du zerlegst den String (TStringList).
-
String replace(String source) { source = StringReplace(source,"N,24","N,32",TReplaceFlags() << rfReplaceAll); return StringReplace(source,"N126,0;N,32;","",TReplaceFlags() << rfReplaceAll); }
-
Danke euch für die raschen Antworten! Leider konnte ich das Problem aber noch nicht lösen.
@Th69 + @chronol
Leider geht das darum nicht, weil "N126,0;N,24;" nur dann gelöscht werden soll, wenn unmittelbar vorher "N??,0;N,24;N??,0;N,24;N??,0;N,24;" (Fragezeichen durch beliebige Ziffern ersetzt) steht. Es gibt also auch Fälle, wo das in alter Form stehenbleiben muss.
Umgekehrt ist es auch so, dass "N,24" nur dann durch "N,32" ersetzt werden soll, wenn unmittelbar danach das zu löschende "N126,0;N,24;" steht.
Habt ihr sonst noch eine Idee?@lister
Ja, ich weiss ja schon was ich ersetzen will, ich komme bloss nicht darauf, wie ich das in Code formalisieren kann. Nochmals in etwas anderen Worten. Ich will jedes Vorkommen von
"N??,0;N,24;N??,0;N,24;N??,0;N,24;N126,0;N,24;"
ersetzen durch
"N??,0;N,32;N??,0;N,32;N??,0;N,32;".
wobei die ?? variable Ziffern darstellen. (Es sind im neuen umgeformten String natürlich der Reihe nach die gleichen Werte)Hat jemand eine Idee?
Viele Grüsse
-
Klingt nach einem guten Anwendungsfall für reguläre Ausdrücke. Für Delphi und C++Builder gibt es da beispielsweise die TPerlRegEx-Komponente.
-
@audacia
Wegen TPerlRegEx guck ich mal, danke für den Tipp!Also so weit bin ich schon gekommen:
AnsiString Daten; AnsiString Fund; AnsiString Fundersatz; int FundStartPos; AnsiString Wert1; AnsiString Wert2; AnsiString Wert3; while (<EINE FUNKTION, DIE DIE STARTPOSITON VON ERSTEM VORKOMMEN VON "N??,0;N,24;N??,0;N,24;N??,0;N,24;N126,0;N,24;" IN DATEN ZURÜCKGIBT>!=0) { FundStartPos=<EINE FUNKTION, DIE DIE STARTPOSITON VON ERSTEM VORKOMMEN VON "N??,0;N,24;N??,0;N,24;N??,0;N,24;N126,0;N,24;" IN DATEN ZURÜCKGIBT>; Fund=Daten.SubString(FundStartPos,45); /*Dummytest, so könnte "Fund" aussehen*/ Fund="N34,0;N,24;N67,0;N,24;N23,0;N,24;N126,0;N,24;"; /*Dummytest*/ Wert1=Fund.SubString(2, 2); Wert2=Fund.SubString(13, 2); Wert3=Fund.SubString(24, 2); Fundersatz="N"+Wert1+",0;N,32;N"+Wert2+",0;N,32;N"+Wert3+",0;N,32;"; Daten=StringReplace(Daten, Fund, Fundersatz, TReplaceFlags() << rfReplaceAll); }
Ich hoffe das würde so funktonieren irgendwie, aber irgendwie muss ich es jetzt noch schaffen nach dem String mit den Platzhaltern zu suchen...
-
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