2017-12-12 5 views
3

Ich lerne immer noch Perl, also Entschuldigung, wenn das eine offensichtliche Frage ist. Gibt es eine Möglichkeit, Text anzupassen, der NICHT in Klammern eingeschlossen ist? Zum Beispiel würde die Suche nach foo nur der zweiten Zeile entsprechen.Passender Text, der nicht in Klammern eingeschlossen ist

(bar foo bar) 
bar foo (
bar foo 
    (bar) (foo) 
) 
+0

verwenden Sie negative Lookahead? – Boschko

Antwort

4

Dies ist sehr weit von "offensichtlich"; andererseits. Es gibt keinen direkten Weg zu sagen "nicht übereinstimmen" für ein komplexes Muster (es gibt eine gute Unterstützung auf einer Zeichenebene, mit [^a], \S usw.). Bei Regex geht es erstens darum, Dinge zusammen zu bringen, nicht darum, sie nicht zu finden.

Ein Ansatz besteht darin, diese (möglicherweise verschachtelten) Trennzeichen zu vergleichen und alles andere als das zu erhalten.

Ein gutes Werkzeug zum Auffinden verschachtelter Trennzeichen ist das Kernmodul Text::Balanced. Wenn es übereinstimmt, kann es uns auch den Teilstring vor dem Match und den Rest des Strings nach dem Match geben.

use warnings; 
use strict; 
use feature 'say'; 

use Text::Balanced qw(extract_bracketed); 

my $text = <<'END'; 
(bar foo bar) 
bar foo (
bar foo 
    (bar) (foo) 
    ) 
END 

my ($match, $before); 
my $remainder = $text; 
while (1) { 
    ($match, $remainder, $before) = extract_bracketed($remainder, '(', '[^(]*'); 
    print $before // $remainder; 
    last if not defined $match; 
} 

Die extract_bracketed kehrt das Spiel, der Rest substring ($remainder) und die Teilkette vor dem Spiel ($before); also passen wir im Rest zusammen.

Aufgenommen von this post, wo es weitere Details gibt und einen anderen Weg, mit Regexp::Common.

+0

Ich wusste nichts über dieses Modul. Vielen Dank! Allerdings finde ich es schwierig, die Zeilennummer zu finden, wenn man innerhalb von $ Text oder $ Lead sucht. Ein Weg könnte darin bestehen, die Anzahl der Newline-Zeichen in "$ match" zu zählen. Aber gibt es einen besseren Weg? – Tohiko

+1

@Tohiko Willkommen. Sie wollen herausfinden, welche Char/Zeile in der Quelle gefunden wird? Das Zählen von '\ n' in $ Lead (oder' $ text', wenn es leer ist) wird nicht darüber informieren, welche Zeile es in der Quelle ist. Ich werde es mir ansehen. – zdim

+0

@Tohiko Beachten Sie, dass ich $ Lead zu $ ​​vor und $ Text zu $ ​​Rest geändert habe – zdim

5

Regex Muster haben ein implizites führenden \G(?s:.)*? („Zeichen überspringen, bis eine Übereinstimmung gefunden wird“). Im Folgenden wird diese Definition so erweitert, dass verschachtelte Parens als zu überspringendes Zeichen betrachtet werden.

while (
    $string =~ m{ 
     \G (?&MEGA_DOT)*? 

     (foo) 

     (?(DEFINE) 
     (?<MEGA_DOT> [^()] | \((?&MEGA_DOT)*+ \)) 
    ) 
    }xg 
) { 
    say "Found a match at pos $-[1]."; 
} 
+0

Bewundernswert! Aber ich denke, das ist ein Grund, warum Leute Angst bekommen, wenn sie Perl riechen ... ;-) – PerlDuck

+1

@PerlDuck, Eigentlich ist es erstaunlich, wie einfach und strukturiert Perl das gemacht hat. – ikegami

+0

Als Anfänger konnte ich dies nicht verstehen und stelle daher freundlicherweise eine detaillierte Methodik zur Verfügung. – ssr1012

Verwandte Themen