[RegEx] Anführungszeichen innerhalb von Anführungszeichen ersetzen.

DerTiger

Lt. Junior Grade
Registriert
Jan. 2006
Beiträge
364
Hi,

ich habe ein umfangreiches XML in das sich Fehler eingeschlichen haben. Konkret geht es um folgendes:

<XML STRUKTUR> ... 19376.3.276.1.5.8" displayName="Anforderung" level="0" type="L"><desc language="de-DE"><span style="font-size:11.0pt;font-family: "Calibri",sans-serif; mso-fareast-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman"; color:#1F497D;mso-ansi-language:DE;mso-fareast-language:EN-US;mso-bidi-language: AR-SA">Anforderung von Diagnostik oder therapeutischen Interve ... <XML STRUKTUR>

Das ganze mehrmals innerhalb des XML

Hier müsste ich eine RegEx bauen die innerhalb der Anführungszeichen alle Anführungszeichen durch &#34; ersetzt.

<span style="font-size:11.0pt;font-family: &#34;Calibri&#34;,sans-serif; mso-fareast-font-family:&#34;Times New Roman&#34;;mso-bidi-font-family:&#34;Times New Roman&#34; color:#1F497D;mso-ansi-language:DE;mso-fareast-language:EN-US;mso-bidi-language: AR-SA">


leider tüftel ich jetzt schon seit geraumer Zeit und komm nicht zum Ergebnis.

*updates kommen noch, ausversehen auf speichern statt Vorschau geklickt...

Update:

Das ganze lässt sich ja mit (<span style=")(.*)(">) abgreifen

irgendwas müsste ich jetzt mit der zweiten Capture Group anstellen um die " ersetzt zu bekommen, aber ich komm einfach nicht weiter.
 
Zuletzt bearbeitet:
ich glaube, alleine mit regex kommste da nicht ran. da das style-attribut aller wahrscheinlichkeit oefters mal mehrere anfuehrungszeichen beinhalten koennte, wuerde ich folgendes versuchen:

regex-substitution von style="([^>]*)"([^>]*)"> auf style="$1&#34;$2"> durchfuehren. und zwar immerwieder, bis der ergebnisstring sich nicht mehr aendert. dazu brauchste 'ne programmiersprache und iteration.

das funktioniert natuerlich nur, wenn das attribut auch das letzte im tag ist - also "> ueberhaupt stimmt. und es funktioniert erst mal nur fuer das style-attribut.

nachwurf: die xml entity in allen ehren, aber wuerde ein apostroph (') nicht auch funktionieren?
 
State (Die Bedeutung eines Zeichen ist je nach Kontext eine andere) mit regex ist schwierig bis unmöglich, dafür gibt es yacc und lex bzw. clone.
Evtl. gibt es einen Quirk weil deine XML auch andere (nicht garantierte) Eigenschaften hat.
 
Mm?
Kontext ist doch Parade für Regex. Warum soll das plötzlich nicht gehen?

Aber der Ansatz stört womöglich. Mein erster Impuls nach dem Muster oben wäre, nach doppelten Anführungsstriche zu suchen, die eben NICHT ins XML Schema passen.
1. doppelpunkt, optional Whitespace, Anführungszeichen? Ersetzen mit dem Doppelpunkt und einem einfachen solchen.
2. Ende ist etwas aufwendiger. Es geht um doppelte AS wo danach weiterer css Code kommt.
CSS Code hat keine = drin. Ergo ist jedes “ wo danach A-Z+ sowie Dash und ein Doppelpunkt folgt zu ersetzen, denn das wäre die fortgesetzte style Liste. Genauer: kommt nach dem “ ein css Attribut, dann war es zu ersetzen.

Beides sollte zur Sicherheit in einen gemeinsamen Ausdruck - schließlich geht es um Textfragmente wo explizit beides zutreffen muß.

Note, letzteres funktioniert so nur, wenn der Taginhalt selber konsistent ist. Wenn da auch “ und ‘ verwechselt werden wird’s umständlicher.

Aber natürlich nicht unmöglich. Regex ist turingvollständig.
 
  • Gefällt mir
Reaktionen: DerTiger
RalphS schrieb:
Mm?
Kontext ist doch Parade für Regex. Warum soll das plötzlich nicht gehen?

Kurz und knapp:
"Regular Expression" ist eine Sprache regulärer Grammatik.
Solche Sprachen können von sogenannten "Kellerautomaten" verarbeitet werden.
So ein Kellerautomat ist ein (nicht-)deterministischer endlicher Automat (NEA/DEA)
NEA/DEA akzeptieren als Input eine theoretisch unendliche Folge von Zeichen, können diese jedoch nur von Start aus Richtung Ende verarbeiten.
Ein Kellerautomat verfügt, im Unterschied zu "normalen" NEA/DEA, über einen unendlich großen Stack für seine Zustände.
Mit seinem Stack kennt der Automat jedoch immer nur seinen letzten Zustand.
Die relevante Implikation für das Problem des TE ist, dass so ein Kellerautomat (und jeder andere NEA/DEA und damit Reguläre Sprache einer regulären Grammatik) keine Schleifen drehen und auch nicht zählen kann. (*)

Zum definitiven Lösen des Problems bedarf es aber mindestens der Fähigkeit, zählen zu können (auch bei Verwendung einer RegEx in einer Schleife).

Die einfachste Lösung wäre wahrscheinlich, ein Pattern zu formulieren, das ein entsprechendes, fehlerhaftes doppeltes Anführungszeichen eindeutig findet und alle Vorkommnisse durch den Match mit ersetztem Anführungszeichen ersetzt.
Das muss dann so oft wiederholt werden, bis es keinen Match mehr gibt. (siehe @bog 's Antwort)
Bei einigen wenigen Dateien mit überschaubarem Inhalt geht das mit einem entsprechenden Editor per Hand.
Bei mehr sollte man dann über Automatisierung nachdenken und u.U. bei gehäuftem Vorkommen einen eigenen Parser schreiben.


(*):
Diese Beschreibung ist jetzt nicht 100%ig richtig und vollständig, aber ich wollte keinen Wall-Of-Text generieren.
Vor allem entspricht es auch nicht mehr 100%ig dem "Geist der Zeit", weil einige "Regular Expressions" mittlerweile auch sowas wie look ahead, look behind oder auch conditionals und recursion kennen und verarbeiten können.
Das sind dann aber meist keine echten NEA/DEA.
Mir ist da nur von Google bekannt, eine vollständige und moderne Regular Expression über eine echte NEA/DEA implementiert zu haben: https://github.com/google/re2
Da kenne ich aber nicht den kompletten Funktionsumfang und bin grad auch nicht dazu in der Lage darüber zu Sinnieren, ob ein NEA/DEA look ahead, look behind oder auch conditionals und recursion können "darf".
Aber das ist jetzt ja auch nicht relevant für das Problem des TE.

Edit:
Googles Implementation kann kein "look ahead", "look behind", "conditionals" oder "recursion".
 
Zuletzt bearbeitet: (Links)
  • Gefällt mir
Reaktionen: DerTiger, heulendoch und Sonnenkarma
Danke schon mal an alle die geantwortet haben, ich habs jetzt anders gemacht.

ich hab mir die Werte für (: "(.*)" ) ausgeben lassen, waren tatsächlich nur eine Hand voll, und dann einfach ersetzt "(Calibri|Times new Roman|...)" --> &#34;$1&#34;
 
Natürlich sind Regex-Engines keine vollwertigen Parser und kommen gerade in XML/HTML schnell an ihre Grenzen. Wenn wir aber davon ausgehen dass dein Dokument abseits des betrachteten Fehlers korrekt formatiert ist kann man sich schon einen Regex zusammenbasteln der für den konkreten Fall ausreicht. Das wird dann zwar kein klassisch-regulärer Ausdruck sein, aber einer den moderne Regex-Engines verarbeiten können. Wir benötigen entweder Subexpression Calls (geht nur in Programmiersprachen, nicht in Editoren) oderVariable-Width-Lookbehind Expressions (unterstützen nur wenige Regex-Engines).

Entscheidend ist sich klar zu machen woran ein Programm korrekte von inkorrekten Anführungszeichen unterscheiden soll. Dazu soll uns die XML-Struktur helfen. Sagen wir vereinfacht die zu untersuchende Struktur ist
name="irgendwas">
oder
name="irgendwas" name2="irgendwas"
(in Worten bspw: alphanum + = + " + egal + " + >)

Vor unserem zu korrigierenden Ausdruck muss also mindestens \w=" stehen, nach unserem Ausdruck muss in etwa (" \w+="|" ?\/?>) stehen. Das ist selbstverständlich kein vollständiges Parsen und muss je nach Dokument angepasst werden (bspw wenn in dem Dokument einfache statt doppelte Anführungszeichen benutzt wurden). Damit haben wir den Ausdruck vor und nach dem Teil den wir bearbeiten wollen und bearbeiten den Teil zwischen den Klammern mit Hilfe eines Funktionsaufruf für den Unterausdruck (.*?). Dieser ersetzt stupide Anführungszeichen in HTML-Äquivalente. Hier der komplette Ausdruck in Ruby von der Kommandozeile aus:

Bash:
ruby -e 'print STDIN.read.gsub(/(?<=\w=")(.*?)(?=" \w+="|" ?\/?>)/){ $&.gsub(/"/, "&#34;") }' < datei

Wenn man keine Programmiersprache zur Hand hat, aber einen Editor der reguläre Ausdrücke mit Lookbehind-Ausdrücken variabler Länge ausführen kann (möglicherweise funktioniert dies in aktuellen Electron-basierenden Editoren wie VSCode oder Atom), dann lässt sich ein etwas länglicher Ausdruck basteln. Wo wir im vorherigen Ausdruck nach sämtlichem Text zwischen dem ersten und letzten Anführungszeichen gesucht haben, müssen wir jetzt speziell nach allen Anführungszeichen in eben diesem Text suchen. Ein solches Anführungszeichen hat genau ein XML-Fragment, in dem das äußere Anführungszeichen enthalten ist, vor sich und nach sich. Eine einfache Suche mit Fragment.".*Fragment würde über andere Fragmente springen, weswegen wird mit folgenden Lookarounds präzisieren: Fragment(.(?!Fragment)) (alles bis zum ersten vorausgehenden Fragment) und ((?<!Fragment).)*Fragment (alles bis zum ersten folgenden Fragment). Die Ausdrücke in den Klammern sind quasi spezialisierte Punkte. Hier der komplette Ausdruck in JavaScript, in einem Editor müsste der //-Teil ins Suchen-, der ""-Teil ins Ersetzen-Feld eingefügt werden:

Javascript:
text.replace(/(?<=\w="(?:.(?!\w="))*)"(?=(?:(?<!" \w+="|" ?\/?>).)*(?:" \w+="|" ?\/?>))/g, "&#34;")
 
  • Gefällt mir
Reaktionen: DerTiger

Ähnliche Themen

J
Antworten
5
Aufrufe
1.922
Jonny9904
J
Antworten
51
Aufrufe
4.426
Zurück
Oben