regex_replace $1
-
Moin,
mein Versuch
std::string s ("Name `name` `name` Vname `vname` `vname` "); map<string, string> stash = {{"name","Ulrich"},{"vname","Hansel"}}; std::regex e ( "`(\\w+)`" ) ; std::cout << std::regex_replace (s, e, stash[$1]);
quittiert der Compiler mit
'$1' was not declared in this scope
. Wie komme ich denn sonst an den ge-match-ten Ausdruck? Bin etwas verwirrt.Bitte um Hinweise und danke im Vorab.
-
Geht das hier? Bin nicht fit bei regular expressions.
std::string s ("Name `name` `name` Vname `vname` `vname` "); map<string, string> stash = {{"name","Ulrich"},{"vname","Hansel"}}; std::regex e ( "`(\\w+)`" ) ; for( auto const& [key,value] : stash ) { std::cout << std::regex_replace (s, e, value ); }
-
@_ro_ro Was soll den
$1
sein? Das ist offenbar eine nicht deklarierte Variable, zumindest denkt der Compiler das. Was erwartest du?
-
da stehen die Matches drin (Platzhalter), so seh eich die:
string callback(string key){ map<string, string> stash = {{"name","Ulrich"},{"vname","Hansel"}}; return key; } int main(){ cout << "Content-Type: text/plain; Charset=UTF-8\n\n"; std::string s ("Name `name` `name` Vname `vname` `vname` "); std::regex e ( "`(\\w+)`" ) ; cout << std::regex_replace (s, e, callback("$1")); // Name name name Vname vname vname return 0; }
Nur das Ersetzen funktioniert nicht. Da löst sich $1 in Luft auf.
MFG
-
@_ro_ro
C++ ist nicht perl, versuche nicht perl in C++ zu programmieren. Und gewöhn dir doch bitte mal an, deine Probleme zu beschreiben.- was möchtest du erreichen?
- was hast du gemacht?
- welches Ergebnis erwartest du?
- was ist dein aktuelles Ergebnis?
-
@DocShoe sagte in regex_replace $1:
auto const& [key,value]
error: expected unqualified-id before '[' token for( auto const& [key,value] : stash ) {
-
Das was @DocShoe sagt.
In Perl ist $1 der erste Match, in c++ gibt es das so nicht.@_ro_ro , für den Vorschlag von @DocShoe musst du C++ 17 bei dem Compiler enabled haben.
Mit c++ 11 sollte das gehen (ungetestet)for( auto const& s : stash ) { std::cout << std::regex_replace (s, e, s.second ); }
-
@Schlangenmensch sagte in regex_replace $1:
Das was @DocShoe sagt.
In Perl ist $1 der erste Match, in c++ gibt es das so nicht.doch gibt es:
https://cplusplus.com/reference/regex/regex_replace/
mfg
-
@_ro_ro sagte in regex_replace $1:
@Schlangenmensch sagte in regex_replace $1:
Das was @DocShoe sagt.
In Perl ist $1 der erste Match, in c++ gibt es das so nicht.doch gibt es:
https://cplusplus.com/reference/regex/regex_replace/
mfg
Nope. Nicht so wie du es geschrieben hast. Du musst schon genauer lesen.
Aus deinem link:std::regex_replace (std::back_inserter(result), s.begin(), s.end(), e, "$2");
Und hier ist deine zeile
std::regex_replace (s, e, stash[$1]);
Das ist überhaupt nicht identisch was den 3. Parameter betrifft.
-
fast. Aber da ist noch was im Argen:
std::string s ("Name `name` Vname `vname` "); map<string, string> stash; stash["name"] = "Hansel"; stash["vname"] = "Ulrich"; std::regex e ( "`(\\w+)`" ) ; for( auto const& st : stash ) { std::cout << std::regex_replace (s, e, st.second ) << "\n"; }
Gibt aus:
Name Hansel Vname Hansel Name Ulrich Vname Ulrich
Also stimmt mit der Zurodnung was nicht. Was ich beabsichtige sollte eigentlich klar sein: Das Template soll ja nur einmal (!) gerendert werden und mit dem Ersetzungen am rechten Platz.
(!) Da ist die Schleife ja schon ein falscher Ansatz
Name Hansel Vname Ulrich
Viele Grüße!
-
std::regex_replace gibt den geänderten String zurück, deine Schleife operiert aber immer nur auf dem Originalstring
s
. Du mußt alsos
neu zuweisen.
-
Ich glaube, du kannst immer nur ein Vorkommen gleichzeitig ersetzen, dazu musst du doch alle Einträge per Schleife durchlaufen und separat ersetzen:
std::string const templ = "Name `name` Vname `vname`"; std::map<std::string, std::string> const stash { { "name", "Hansel" }, { "vname", "Ulrich" } }; std::string output = templ; for( auto const& [key, value] : stash ) { std::regex r( "`" + key + "`" ); output = std::regex_replace( output, r, value ); } std::cout << output << "\n";
-
@Th69 sagte in regex_replace $1:
std::regex_replace gibt den geänderten String zurück, deine Schleife operiert aber immer nur auf dem Originalstring
s
. Du mußt alsos
neu zuweisen.Ja is schon klar. Die Frage ist nur wie
MFG
-
Moin!
- Regex-Strings kann man in C++ am besten mit einem R-String schreiben, damit man nicht überall die Backslashes quoten muss. Zum Beispiel so:
Dein
"`(\\w+)`" // ist R"(`(\w+)`)" // oder auch: R"***(`(\w+)`)***"
Du kannst den String zwischen " und ( so wählen, dass das Ende-Zeichen sicher nicht im String vorkommt.
- Regexes in C++ benutzen sich so "mäh", wenn man das aus Perl gewöhnt ist. Man kann mit einem
regex_token_iterator
arbeiten, ungefähr so:
string text { "Name `name` Vname `vname` " }; regex re { R"*(`(\w+)`)*" }; for_each( sregex_token_iterator(begin(text), end(text), re, {0, -1}), sregex_token_iterator{}, [](const string &s){cout << s << "\n";} );
Ist aber blöd, weil du abwechselnd die Matches und Zwischenteile bekommst (oder wahlweise nur die Matches, wenn du das -1 weglässt). Ich glaube, dass man sich da also was selbst basteln muss (vielleicht auch nur mit dem regex_iterator?). Ich habe std::regex immer versucht zu vermeiden.
- Weil man oft gar keine regexes braucht, die man zur Laufzeit zusammenstellen kann (ist eh recht teuer), gibt es auch eine spannende Compilezeit-Regex-Bibliothek von Hanka Dusikova: https://github.com/hanickadot/compile-time-regular-expressions
- Regex-Strings kann man in C++ am besten mit einem R-String schreiben, damit man nicht überall die Backslashes quoten muss. Zum Beispiel so:
-
Danke @wob das wird ne wüste Frickelei
Also mir geht es darum, meine Templating Engine performanter zu machen. Die geht Zeichen für Zeichen durch das Template um die String-Begrenzer (da habe ich mich für Backticks entschieden) und damit die Platzhalter zu finden. Das Ganze läuft stabil und ermöglicht eine Fehlerbehandlung wenn die Syntax des Template nicht stimmt.
Wahrscheinlich ists das Beste, es so zu belassen. Performance ist nicht immer das Kriterium.
Viele Grüße.
-
@_ro_ro In Boost geht das, was du willst: https://www.boost.org/doc/libs/1_84_0/libs/regex/doc/html/boost_regex/ref/regex_replace.html (das
Formatter
kann dort eine Funktion sein)
-
@wob sagte in regex_replace $1:
@_ro_ro In Boost geht das, was du willst: https://www.boost.org/doc/libs/1_84_0/libs/regex/doc/html/boost_regex/ref/regex_replace.html (das
Formatter
kann dort eine Funktion sein)Ja danke Dir. Boost kenn' ich auch schon
Und wie nochmal verspeist man einen Elefanten? Stück für Stück
Viele Grüße1!!
-
Das ist doch kein Hexenwerk, sich sowas schnell selbst zu schreiben.
-
@DocShoe sagte in regex_replace $1:
Das ist doch kein Hexenwerk, sich sowas schnell selbst zu schreiben.
Naja, ich finde es nicht so einfach. Diese Funktionalität fehlt einfach in std::regex. Klar, für diesen speziellen Fall hat man selbst schnell was geklöppelt.
Oder eben mit boost:
#include <boost/regex.hpp> #include <map> #include <string> #include <iostream> int main() { using namespace std; map<string, string> repl = {{"name", "Ulrich"}, {"vname", "Hansel"}}; string text { "Name `name` Vname `vname` " }; boost::regex re { R"*(`(\w+)`)*" }; string result = boost::regex_replace(text, re, [&](const boost::smatch &m) { return repl.at(m[1]); } ); cout << result << '\n'; }
-
Ja, sehr schön. Wie sieht die Fehlerbehandlung aus wenn im Template die Syntax nicht stimmt? Wenn z.B. an einem Platzhalter ein Backtick vergessen wurde?
MFG
-
Du meinst, wenn kein Text ersetzt werden konnte?
Entweder (bezogen auf den Code von @wob) den String vergleichen:if (result == text) // ...
oder aber ein Flag setzen:
bool changed = false; string result = boost::regex_replace(text, re, [&](const boost::smatch &m) { changed = true; return repl.at(m[1]); } ); if (!changed) // ...