webentwicklung-frage-antwort-db.com.de

Wie kann man ein bestimmtes Zeichen finden und ersetzen, wenn es nur in Anführungszeichen steht?

Problem: Ich habe Tausende von Dokumenten, die einen bestimmten Charakter enthalten, den ich nicht möchte. Z.B. das Zeichen a. Diese Dokumente enthalten verschiedene Zeichen, aber die a, die ich ersetzen möchte, sind in Anführungszeichen oder einfache Anführungszeichen gesetzt. 

Ich würde sie gerne finden und ersetzen, und ich dachte, Regex wäre nötig. Ich verwende VSCode, bin aber offen für Vorschläge. 

Mein Versuch: Ich konnte den folgenden regulären Ausdruck finden, der mit einer bestimmten Zeichenfolge übereinstimmt, die die Werte im () enthält.

".*?(r).*?"

Dies hebt jedoch nur das gesamte Zitat hervor. Ich möchte nur das Zeichen hervorheben.

Jede Lösung, möglicherweise außerhalb von Regex, ist willkommen.

Beispielergebnisse: Das Zeichen lautet a, find replace zu b.

Somebody once told me "apples" are good for you => Somebody once told me "bpples" are good for you

"Aardvarks" make good kebabs => "Abrdvbrks" make good kebabs

The boy said "aaah!" when his mom told him he was eating aardvark => The boy said "bbbh!" when his mom told him he was eating aardvark

12
Ka Mok

Visual Studio Code

VS Code verwendet die JavaScript-RegEx-Engine für die Such-/Ersetzungsfunktion. Dies bedeutet, dass Sie bei der Arbeit mit Regex im Vergleich zu anderen Varianten wie .NET oder PCRE sehr eingeschränkt sind.

Glücklicherweise unterstützt diese Variante Lookaheads, und mit Lookaheads können Sie Suchen, aber nicht Konsum Zeichen. Eine Möglichkeit, um sicherzustellen, dass wir uns in einer Anführungszeichenfolge befinden, besteht darin, nach der Anzahl der Anführungszeichen am Ende der Datei-/Betreffzeichenfolge zu suchen, die nach dem Abgleichen einer a ungerade ist:

a(?=[^"]*"[^"]*(?:"[^"]*"[^"]*)*$)

Live Demo

Dies sucht nach as in einer doppelten Anführungszeichenfolge, damit alle Zeichen in Anführungszeichen alle "s durch ' ersetzen. Sie können nicht beides gleichzeitig haben.

Es gibt jedoch ein Problem mit Regex oben, dass es mit doppelten Anführungszeichen in doppelten Anführungszeichen in Konflikt steht. Um sie auch zusammenzubringen, wenn es darauf ankommt, haben Sie einen langen Weg vor sich:

a(?=[^"\\]*(?:\\.[^"\\]*)*"[^"\\]*(?:\\.[^"\\]*)*(?:"[^"\\]*(?:\\.[^"\\]*)*"[^"\\]*(?:\\.[^"\\]*)*)*$)

Die Anwendung dieser Ansätze auf große Dateien führt wahrscheinlich zu einem Stapelüberlauf, sodass wir einen besseren Ansatz sehen.

Ich verwende VSCode, bin aber offen für Vorschläge.

Das ist großartig. Dann würde ich vorschlagen, awk oder sed oder etwas programmatischer zu verwenden, um das zu erreichen, was Sie wollen, oder wenn Sie Sublime Text verwenden können, gibt es eine Möglichkeit, dieses Problem auf elegante Weise zu umgehen.

Erhabener Text

Dies soll bei großen Dateien mit Hunderttausenden von Zeilen funktionieren, aber es ist zu beachten, dass es für ein einzelnes Zeichen (hier a) funktioniert, das mit einigen Änderungen auch für ein Word oder einen Teilstring funktionieren kann:

Suchen nach:

(?:"|\G(?<!")(?!\A))(?<r>[^a"\\]*+(?>\\.[^a"\\]*)*+)\K(a|"(*SKIP)(*F))(?(?=((?&r)"))\3)
                           ^              ^            ^

Ersetzen Sie es mit: WHATEVER\3

Live Demo

RegEx-Aufschlüsselung:

(?: # Beginning of non-capturing group #1
    "   # Match a `"`
    |   # Or
    \G(?<!")(?!\A)  # Continue matching from last successful match
                    # It shouldn't start right after a `"`
)   # End of NCG #1
(?<r>   # Start of capturing group `r`
    [^a"\\]*+   # Match anything except `a`, `"` or a backslash (possessively)
    (?>\\.[^a"\\]*)*+   # Match an escaped character or 
                        # repeat last pattern as much as possible
)\K     # End of CG `r`, reset all consumed characters
(   # Start of CG #2 
    a   # Match literal `a`
    |   # Or
    "(*SKIP)(*F)    # Match a `"` and skip over current match
)
(?(?=   # Start a conditional cluster, assuming a positive lookahead
    ((?&r)")    # Start of CG #3, recurs CG `r` and match `"`
  )     # End of condition
  \3    # If conditional passed match CG #3
 )  # End of conditional

 enter image description here

Drei-Schritte-Ansatz

Zu guter Letzt...

Das Übereinstimmen eines Zeichens in Anführungszeichen ist schwierig, da die Trennzeichen genau gleich sind. Daher können Öffnungs- und Schließmarken nicht voneinander unterschieden werden, ohne die benachbarten Zeichenfolgen zu betrachten. Sie können ein Trennzeichen in etwas anderes ändern, damit Sie es später suchen können.

Schritt 1:

Suche nach: "[^"\\]*(?:\\.[^"\\]*)*"

Ersetzen durch: $0Я

Schritt 2:

Suche nach: a(?=[^"\\]*(?:\\.[^"\\]*)*"Я)

Ersetzen Sie mit was Sie erwarten.

Schritt 3:

Suche nach:

Ersetze nichts, um alles zurückzusetzen.


10
revo

Zunächst einige Überlegungen:

  1. Ein einzelnes Anführungszeichen kann mehrere a Zeichen enthalten.
  2. Jedes Anführungszeichen (mit einfachen oder doppelten Anführungszeichen) besteht aus einem Anfangszeichen, einem Text und dem schließenden Anführungszeichen dasselbe. Ein einfacher Ansatz besteht darin, anzunehmen, dass die ungeraden Anführungszeichen öffnende Anführungszeichen und die geraden Anführungszeichen schließende Anführungszeichen sind, wenn die Anführungszeichen nacheinander gezählt werden.
  3. Nach Punkt 2 könnte es sich lohnen, noch einmal darüber nachzudenken, ob Strings in einfachen Anführungszeichen zulässig sein sollten. Siehe folgendes Beispiel: It's a shame 'this quoted text' isn't quoted. Hier würde der einfache Ansatz annehmen, dass zwei Zeichenfolgen in Anführungszeichen stehen: s a shame Und isn. Ein anderer: This isn't a quote ...'this is' and 'it's unclear where this quote ends'. Ich habe es vermieden, diese Komplexität in Angriff zu nehmen, und mich für den einfachen Ansatz entschieden.

Die schlechte Nachricht ist, dass Punkt 1 ein kleines Problem darstellt, da eine Erfassungsgruppe mit einem Platzhalter-Wiederholungszeichen (z. B. (.*)*) Nur das zuletzt erfasste "Ding" erfasst. Aber die gute Nachricht ist, dass es einen Weg gibt, dies in gewissen Grenzen zu umgehen. Viele Regex-Engines erlauben bis zu 99 Erfassungsgruppen (*). Wenn wir also davon ausgehen können, dass nicht mehr als 99 as in jedem Zitat enthalten sind, [~ # ~] update [~ # ~] ... oder auch wenn wir nicht - siehe Schritt 3), können wir Folgendes tun ...

(*) Meine erste Anlaufstelle, Notepad ++, ist leider keine - es sind nur bis zu 9 zulässig. Ich bin mir nicht sicher, was VS-Code angeht. Aber regex101 (wird für die folgenden Online-Demos verwendet).

TL; DR - Was tun?

  1. Suche nach: "([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*"
  2. Ersetzen durch: "\1\2\3\4\5\6\7\8\9\10\11\12\13\14\15\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31\32\33\34\35\36\37\38\39\40\41\42\43\44\45\46\47\48\49\50\51\52\53\54\55\56\57\58\59\60\61\62\63\64\65\66\67\68\69\70\71\72\73\74\75\76\77\78\79\80\81\82\83\84\85\86\87\88\89\90\91\92\93\94\95\96\97\98\99"
  3. (Wiederholen Sie optional die vorherigen beiden Schritte, wenn in einem einzelnen Anführungszeichen möglicherweise mehr als 99 solcher Zeichen vorkommen, bis sie alle ersetzt wurden).
  4. Wiederholen Sie Schritt 1, aber ersetzen Sie alle " Durch ' Im regulären Ausdruck, d. H .: '([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*'
  5. Wiederholen Sie die Schritte 2-3.

Online-Demos

Bitte sehen Sie sich die folgenden regex101-Demos an, mit denen die Ersetzungen tatsächlich durchgeführt werden können, wenn Sie den gesamten Text in den Inhalt von "TEST STRING" kopieren können:

2
Steve Chambers
/(["'])(.*?)(a)(.*?\1)/g

Mit dem Ersetzen-Muster:

$1$2$4

Soweit mir bekannt ist, verwendet VS Code dieselbe Regex-Engine wie JavaScript, weshalb ich mein Beispiel in JS geschrieben habe.

Das Problem dabei ist, dass, wenn Sie mehrere Einsen in einem Satz von Anführungszeichen haben, es schwierig wird, die richtigen Werte herauszuholen, so dass hinter dem Code eine Art Code stehen muss, oder Sie müssen den Ersetzen-Button bis zum Anschlag hämmern Übereinstimmungen werden gefunden, um das Muster zu rekursieren und alle Anführungszeichen zwischen den Anführungszeichen zu entfernen

let regex = /(["'])(.*?)(a)(.*?\1)/g,
subst = `$1$2$4`,
str = `"a"
"helapke"
Not matched - aaaaaaa
"This is the way the world ends"
"Not with fire"
"ABBA"
"abba",
'I can haz cheezburger'
"This is not a match'
`;


// Loop to get rid of multiple a's in quotes
while(str.match(regex)){
    str = str.replace(regex, subst);
}

const result = str;
console.log(result);

1
Kyle Fairns

Wenn Sie Visual Studio (anstelle von Visual Studio-Code) verwenden können, ist es in C++ und C # geschrieben und verwendet die regulären .NET Framework-Ausdrücke . Dies bedeutet, dass Sie Lookbehinds mit variabler Länge verwenden können, um dies auszuführen. 

(?<="[^"\n]*)a(?=[^"\n]*")

Durch Hinzufügen weiterer Logik zu dem obigen regulären Ausdruck können Sie ihm sagen, alle Positionen zu ignorieren, an denen geradzahlige " vor ihm liegen. Dies verhindert Übereinstimmungen für a außerhalb von Anführungszeichen. Nehmen Sie zum Beispiel die Zeichenfolge "a" a "a". Nur das erste und das letzte a in dieser Zeichenfolge werden abgeglichen, aber das mittlere wird ignoriert.

(?<!^[^"\n]*(?:(?:"[^"\n]*){2})+)(?<="[^"\n]*)a(?=[^"\n]*")

Jetzt besteht das einzige Problem darin, dass dies zerbrochen wird, wenn wir " innerhalb zweier Anführungszeichen wie "a\"" a "a" abgehängt haben. Wir müssen mehr Logik hinzufügen, um dieses Verhalten zu verhindern. Glücklicherweise existiert diese schöne Antwort für das richtige Zuordnen von ". Durch Hinzufügen dieser Logik zum Regex oben erhalten wir Folgendes:

(?<!^[^"\n]*(?:(?:"(?:[^"\\\n]|\\.)*){2})+)(?<="[^"\n]*)a(?=[^"\n]*")

Ich bin nicht sicher, welche Methode am besten für Ihre Zeichenfolgen geeignet ist, aber ich werde den letzten Regex ausführlich erläutern, da er auch die beiden vorherigen erklärt.

  • (?<!^[^"\n]*(?:(?:"(?:[^"\\\n]|\\.)*){2})+) Ein negativer Look, der sicherstellt, dass das Vorhergehende nicht mit dem folgenden übereinstimmt
    • ^ Position am Zeilenanfang übernehmen
    • [^"\n]* Entspricht beliebiger Anzahl, außer " oder \n
    • (?:(?:"(?:[^"\\\n]|\\.)*){2})+ Gleiche oder mehrmals mit dem folgenden Code übereinstimmen. Dadurch wird sichergestellt, dass vor dem Match ein " steht, dass sie ausgeglichen in dem Sinne sind, dass es ein öffnendes und schließendes Anführungszeichen gibt.
      • (?:"(?:[^"\\\n]|\\.)*){2} Stimmt genau zweimal überein
      • " Dies wörtlich abgleichen
      • (?:[^"\\\n]|\\.)* Entspricht beliebig oft einem der folgenden .______.
        • [^"\\\n] Entspricht nichts anderem als ", \ und \n
        • \\. Entspricht \, gefolgt von einem beliebigen Zeichen
  • (?<="[^"\n]*) Ein positiver Look, der sicherstellt, dass das Vorhergehende mit dem folgenden übereinstimmt
    • " Dies wörtlich abgleichen
    • [^"\n]* Entspricht beliebiger Anzahl, außer " oder \n
  • a Passen Sie dies wörtlich an
  • (?=[^"\n]*") Ein positiver Lookahead, der sicherstellt, dass das Folgende mit dem folgenden übereinstimmt :__.
    • [^"\n]* Entspricht beliebiger Anzahl, außer " oder \n
    • " Dies wörtlich abgleichen

Sie können den \n aus dem obigen Muster entfernen, wie in den folgenden Hinweisen vorgeschlagen. Ich habe es hinzugefügt, nur für den Fall, dass es spezielle Fälle gibt, die ich nicht in Betracht ziehe (d. H. Kommentare), die diese Regex in Ihrem Text brechen könnten. Der \A zwingt den Regex auch dazu, vom Anfang des Strings (oder der Datei) statt vom Zeilenanfang abzugleichen.

(?<!\A[^"]*(?:(?:"(?:[^"\\]|\\.)*){2})+)(?<="[^"]*)a(?=[^"]*")

Du kannst diese Regex hier testen

So sieht es in Visual Studio aus:

 Visual Studio example

1
ctwheels

I am using VSCode, but I'm open to any suggestions. 

Wenn Sie in einer Editor - Umgebung bleiben möchten, können Sie dies verwenden
Visual Studio (> = 2012) oder sogar Notepad ++ zur schnellen Korrektur.
Dadurch wird die Verwendung einer falschen Skriptumgebung vermieden.

Beide Engines (Dot-Net bzw. Boost) verwenden das \G-Konstrukt.
Welches ist fängt das nächste Match an der Position an, an der das letzte aufgehört hat. 

Auch dies ist nur ein Vorschlag. 

Dieser reguläre Ausdruck überprüft nicht die Gültigkeit ausgewogener Anführungszeichen innerhalb des gesamten Dokuments
string vor der Zeit (kann jedoch durch Hinzufügen einer einzelnen Zeile). 

Es geht darum zu wissen, wo die innen und außen von Anführungszeichen liegen. 

Ich habe den Regex kommentiert, aber wenn Sie mehr Informationen benötigen, lassen Sie es mich wissen.
Auch dies ist nur ein Vorschlag (ich weiß, dass Ihr Editor ECMAScript verwendet). 

(?s)(?:^([^"]*(?:"[^"a]*(?=")"[^"]*(?="))*"[^"a]*)|(?!^)\G)a([^"a]*(?:(?=a.*?")|(?:"[^"]*$|"[^"]*(?=")(?:"[^"a]*(?=")"[^"]*(?="))*"[^"a]*))) suchen
$1b$2 ersetzen 

Das ist alles dazu. 

https://regex101.com/r/loLFYH/1

Bemerkungen 

(?s)                          # Dot-all inine modifier
 (?:
      ^                             # BOS 
      (                             # (1 start), Find first quote from BOS (written back)
           [^"]* 
           (?:                           # --- Cluster
                " [^"a]*                      # Inside quotes with no 'a'
                (?= " )
                " [^"]*                       # Between quotes, get up to next quote
                (?= " )
           )*                            # --- End cluster, 0 to many times

           " [^"a]*                      # Inside quotes, will be an 'a' ahead of here
                                         # to be sucked up by this match           
      )                             # (1 end)

   |                              # OR,

      (?! ^ )                       # Not-BOS 
      \G                            # Continue where left off from last match.
                                    # Must be an 'a' at this point
 )
 a                             # The 'a' to be replaced

 (                             # (2 start), Up to the next 'a' (to be written back)
      [^"a]* 
      (?:                           # --------------------
           (?= a .*? " )                 # If stopped before 'a', must be a quote ahead
        |                              # or,
           (?:                           # --------------------
                " [^"]* $                     # If stopped at a quote, check for EOS
             |                              # or, 
                " [^"]*                       # Between quotes, get up to next quote
                (?= " )

                (?:                           # --- Cluster
                     " [^"a]*                      # Inside quotes with no 'a'
                     (?= " )
                     " [^"]*                       # Between quotes 
                     (?= " )
                )*                            # --- End cluster, 0 to many times

                " [^"a]*                      # Inside quotes, will be an 'a' ahead of here
                                              # to be sucked up on the next match                    
           )                             # --------------------
      )                             # --------------------
 )                             # (2 end)
0
sln

"In doppelte Anführungszeichen" ist ziemlich kompliziert, da möglicherweise komplizierte Szenarien in Betracht gezogen werden, um dies vollständig zu automatisieren.

Was sind Ihre genauen Regeln für "in Anführungszeichen eingeschlossen"? Müssen Sie mehrzeilige Angebote berücksichtigen? Haben Sie Strings mit Anführungszeichen oder Anführungszeichen in Anführungszeichen gesetzt, die nicht mit Start-/End-Strichnotierungen verwendet wurden? 

Es kann jedoch ein recht einfacher Ausdruck vorhanden sein, um das zu tun, was Sie wollen.

Suchausdruck: ("[^a"]*)a

Ersetzungsausdruck: $1b

Dies gilt nicht innerhalb oder außerhalb von Anführungszeichen - Sie müssen dies visuell tun. Der Text wird jedoch vom Zitat bis zum übereinstimmenden Zeichen hervorgehoben, sodass Sie schnell entscheiden können, ob sich dieser darin befindet oder nicht.

Wenn Sie mit der visuellen Inspektion leben können, können wir dieses Muster aufbauen, um verschiedene Angebotstypen sowie Groß- und Kleinschreibung aufzunehmen.

0
James