Mehrzeiliges Suchen und Ersetzen

8

Möchten Sie eine Suche durchführen und in einer Datei mit 12000 Zeilen ersetzen.

Insbesondere wenn ein Vorkommen von ^ SetFontSize 28nach einem ^HideBlock und vor dem nächsten ^Hideoder vorhanden ist ^Show, ändern Sie 28zu 18.

Hier ist ein Ausschnitt aus der Originaldatei.

Hide # Gear - Endgame
    ItemLevel >= 77
    Rarity = Magic
    LinkedSockets >= 3
    BaseType "Runic Hatchet"
    SetTextColor 140 190 255 # Magic Item Highlight
    SetFontSize 28

Hide # Gear - Endgame
    ItemLevel >= 77
    Rarity = Magic
    Sockets >= 3
    BaseType "Runic Hatchet"
    SetTextColor 140 190 255 # Magic Item Highlight
    SetFontSize 28

Show # Gear - Endgame
    ItemLevel >= 83
    Rarity = Normal
    Sockets < 3
    BaseType "Tiger Hook"
    SetTextColor 240 240 240 # Normal Item Highlight
    SetBackgroundColor 70 70 70
    SetFontSize 28

Das Endergebnis für einen der HideBlöcke sollte folgendermaßen aussehen:

Hide # Gear - Endgame
    ItemLevel >= 77
    Rarity = Magic
    LinkedSockets >= 3
    BaseType "Runic Hatchet"
    SetTextColor 140 190 255 # Magic Item Highlight
    SetFontSize 18

Ersetzen SetFontSize 28zu SetFontSize 18, aber nur, wenn es in einem ^HideBlock erscheint.

Der böse Regex, den ich versuchte, war: :%s/^Hide\(.*\)SetFontSize 28$/Hide\1SetFontSize 18/g

Aber wurde gesagt, Muster nicht gefunden. Bitte lassen Sie mich wissen, wenn zusätzliche Informationen erforderlich sind oder wenn meine Anfrage unklar ist.

Kerl
quelle
5
Hat jeder HideBlock eine SetFontSizeZeile (was auch immer der Wert sein mag)? Wenn ja, könnten Sie verwenden:%s/Hide\_.\{-\}SetFontSize \zs28/18/
muru
2
@muru whatever be the valuewürde Probleme verursachen, Ihre Lösung funktioniert nur, wenn jeder HideBlock eine SetFontSizeZeile hat und der Wert genau ist 28, andernfalls stimmt er bis 28zu einem anderen Block überein .
Dedowsdi

Antworten:

2

Eine Möglichkeit, dies zu lösen, wäre die Verwendung :globaleiner Bereichsausgabe.

Typische Verwendung des globalBefehls wäre

:[range]g/{pattern}/[cmd]

Es besteht auch die Möglichkeit, dass dieses Muster einen Bereich anstelle einer einzelnen Linienübereinstimmung generiert, indem es ,in Form von verwendet wird

:[range]g/{first pattern}/,/{second pattern}/[cmd]

Dadurch wird ein Bereich generiert, der auf den Befehl angewendet wird.

Für Ihr Beispiel wäre das erste Muster die erste sein passenden HideEintrag und das zweite Muster ist entweder Hide, Showoder das Ende der Datei (vorausgesetzt , Sie wollen, dass letzte ausblenden Fall).

:g/Hide/,/\(Hide\|Show\|\%$\)/s/SetFontSize 28/SetFontSize 18/

Der erste reguläre Ausdruck ist einfach /Hide/. Die zweite Regex enthält einige interessante Teile.

  • \(und \)erstellt eine entsprechende Gruppierung von Atomen
  • \| ist die ODER-Verknüpfung
  • \%$ repräsentiert das Ende der Datei

Sobald wir unsere Bereiche definiert haben, wenden wir Ersatz durch ein Muster und eine Zeichenfolge an, wie Sie es normalerweise tun würden.

Bitte beachten Sie, dass der in diesem Beispiel verwendete reguläre Ausdruck sehr einfach ist. Sie sollten sicherstellen, dass Ihre Kennungen für den Anfang und das Ende des Bereichs die richtigen Bereiche erfassen.

jecxjo
quelle
2

Es sieht so aus, als ob dein böser Regex nicht böse genug war ... :-)

Suchbereich

Die Suche müsste folgendermaßen geändert werden:

^Hide\(\(\(Show\|Hide\)\@!\_.\)*\)SetFontSize 28

Dies beinhaltet einige ungewöhnliche Dinge und so viele Klammern ... Mal sehen, was wir da drin haben:

Das Caret ( ^)

Mit dem Caret wird der Zeilenanfang bezeichnet. Ich denke, wir sind mit diesem bereits vertraut.

Ein wichtiger Punkt, der ^funktioniert nur als erstes Zeichen in Ihrem Muster. Nach dem wird es wörtlich genommen. Um einen Zeilenanfang in Ihren Ausdruck aufzunehmen, müssen Sie ihn verwenden \_^. In unserer Situation brauchten wir das jedoch nicht.

(Es gibt ein ähnliches Phänomen mit $und \_$)

Die erste und letzte Klammer ( \( ... \))

Die erste und die letzte Klammer werden alleine verwendet, was bedeutet, dass alles, was darin erscheint, erfasst und in den Parameter gesetzt wird \1. Sie haben das bereits in Ihrem eigenen regulären Ausdruck verwendet, daher gehe ich davon aus, dass Sie auch mit diesem vertraut sind.

Der zweite Satz in Klammern

Wie Sie vielleicht bemerken, gibt es einen zweiten Satz Klammern, gefolgt von einem Sternchen \( ... \)*. Das heißt, wir suchen nach dem, was beliebig oft passt. Dies ist die übliche Art, das Sternchen zu verwenden, daher sollten Sie damit vertraut sein.

Der dritte Satz von Klammern, OR und \_.

Ja, es gibt tatsächlich drei Klammern vor dem Wort Show. Dieser letzte Satz ist aus zwei Gründen erforderlich: \|dem folgenden und dem folgenden @!.

In Bezug auf die OP-Operation sollten Sie bereits damit vertraut sein.

Show\|Hide    or    Hide\|Show

Die Reihenfolge spielt hier keine Rolle. Das \ist vor dem notwendig |, um in vim zu arbeiten.

Die Klammer um diesen Ausdruck ermöglicht es uns, dem Ausdruck durch etwas zu folgen . Hier die @!.

\(Show\|Hide\)@!

Dieser ist viel weniger vertraut. Es bedeutet, wenn nicht übereinstimmend . Die Verwendung ist zwar nicht sehr einfach, aber Sie müssen diesem Ausdruck das folgen, was Sie extrahieren möchten, das nicht mit dem Ausdruck übereinstimmen sollte. Deshalb haben wir \_.hinter diesem Muster.

Die \_.Mittel passen zu allem. Im Gegensatz zu der .für sich, die nicht zum \nCharakter passt . Mit anderen Worten, wir stimmen mit jedem Zeichen in einer beliebigen Anzahl von Zeilen überein, es sei denn, es stimmt mit Showoder überein Hide.

Beachten Sie, dass die Klammern um diesen Ausdruck ebenso wichtig sind wie das Sternchen. Das Ganze macht es also wirklich möglich:

\(\(Show\|Hide\)@!\_.\)*

was bis zum nächsten aka übereinstimmen Showoder HideZeichen (beachten Sie, dass es würde auch passen Showing, Shower, HideMeetc. sollten Sie verwenden können, \<und \>wenn es notwendig ist , das Wort genau übereinstimmen.)

Randnotiz: Um in mehreren Zeilen zu suchen, können Sie auch das \nZeichen im Muster verwenden. Es ist jedoch nicht so vielseitig als das \_.Muster.

SetFontSize 28

Jetzt muss der Abschnitt SetFontSize 28auch enthalten. Genau wie du es in deiner Regex getan hast. Wenn SetFontSize 28in diesem Abschnitt kein angezeigt wird, wiederholen Sie die Suche im nächsten Abschnitt.

Aufgrund der obigen Negation (die Übereinstimmung außer Showoder Hide) geht die Suche nicht zum nächsten Abschnitt über, wodurch das Risiko besteht, dass sie durcheinander gebracht wird.

Ersatzteil

Der Ersatz ist genauso wie Sie ihn hatten:

.../Hide\1SetFontSize 18/

Wir verwenden die Klammern in der Suche, damit das \1wie erwartet funktioniert.

Vollständige Suche und Ersetzung

Die resultierenden Muster sehen folgendermaßen aus:

:%s/^Hide\(\(\(Show\|Hide\)@!\_.\)*\)SetFontSize 28/Hide\1SetFontSize 18/

Das \(Show\|Hide\)sollte alle möglichen Header enthalten .

Quellen

Regex für jedes Zeichen, einschließlich Zeilenumbruch ( \_.\{-})

Suche nach Zeilen ohne Muster und andere hilfreiche Suchanfragen ( @!)

Vim-Dokumentation: pattern ( \_^)

Alexis Wilke
quelle
1
Ich mag die ()*, meine Version Ihrer Antwort : %s/\v^Hide.*\n(\s+.*\n)*\s*SetFontSize\s+\zs28/16.
Dedowsdi