2016-05-13 15 views
1

Wie kann ich eine Reihe übereinstimmender öffnender/schließender Klammern ersetzen, wenn die erste öffnende Klammer dem Schlüsselwort array folgt? Können reguläre Ausdrücke bei dieser Art von Problemen helfen?RegExp zum Ersetzen passender Klammern in verschachtelter Struktur

Um genauer zu sein, würde Ich mag diese entweder JavaScript oder PHP

// input 
$data = array(
    'id' => nextId(), 
    'profile' => array(
     'name' => 'Hugo Hurley', 
     'numbers' => (4 + 8 + 15 + 16 + 23 + 42)/108 
    ) 
); 

// desired output 
$data = [ 
    'id' => nextId(), 
    'profile' => [ 
     'name' => 'Hugo Hurley', 
     'numbers' => (4 + 8 + 15 + 16 + 23 + 42)/108 
    ] 
]; 
+0

Dies ist nicht die Sprache agno zu erhalten soweit es die Regex betrifft. Nicht viele Regex-Varianten unterstützen die Rekursion, die Sie hier benötigen. Also, welche Regex-Engine verwendest du? –

+0

Leider sind Regex-Syntaxen und -Funktionen nicht sprachunabhängig. –

+0

Balanced Text über Funktionsaufrufe kann in PCRE/Perl erfolgen. Ausgeglichene Gruppen (Zählen) können in Dot-Net durchgeführt werden. Das sind deine Möglichkeiten. Für pcre (und Dot-Net, glaube ich) kann man nur das gesamte Nest von Text bekommen. Um das weiter zu unterteilen, müssen Sie den Inhalt rekursiv analysieren. – sln

Antwort

3

Tim Pietzcker gab die Dot-Net-Zählversion.
Es hat die gleichen Elemente wie die PCRE (php) Version unten.

Alle Vorbehalte sind gleich. Insbesondere Array Klammer muss
ausgeglichen sein, da sie die gleiche schließende Klammer als Trennzeichen verwenden.

Der gesamte Text muss geparst werden (oder sollte sein).
Die äußeren Gruppen 1, 2, 3, 4 ermöglicht es Ihnen, die Teile zu bekommen
CONTENT
CORE-1 array()
CORE-2 jede ()
AUSNAHMEN

Jedes Spiel bekommt man eine dieser äußeren Dinge und schließen sich gegenseitig aus.

Der Trick besteht darin, eine PHP Funktionparse(core) zu definieren, die den CORE analysiert.
Innerhalb dieser Funktion ist die while (regex.search(core) { .. } Schleife.

Jedes Mal entweder CORE-1 oder 2 Gruppen übereinstimmen, rufen die Funktion übergeben parse(core)
den Inhalt dieser Gruppe Kern zu.

Und innerhalb der Schleife, einfach Inhalt ablegen und es dem Hash zuweisen.

Offensichtlich sollte das Konstrukt der Gruppe 1, das (?&content) aufruft,
durch Konstrukte ersetzt werden, um Ihre Hash-ähnlichen variablen Daten zu erhalten.

In einem detaillierten Maßstab kann dies sehr mühsam sein.
Normalerweise müssten Sie jedes einzelne Zeichen korrekt berücksichtigen
die gesamte Sache analysieren.

(?is)(?:((?&content))|(?>\barray\s*\()((?=.)(?&core)|)\)|\(((?=.)(?&core)|)\)|(\barray\s*\(|[()]))(?(DEFINE)(?<core>(?>(?&content)|(?>\barray\s*\()(?:(?=.)(?&core)|)\)|\((?:(?=.)(?&core)|)\))+)(?<content>(?>(?!\barray\s*\(|[()]).)+)) 

Expanded

# 1: CONTENT 
# 2: CORE-1 
# 3: CORE-2 
# 4: EXCEPTIONS 

(?is) 

(?: 
     (         # (1), Take off CONTENT 
      (?&content) 
    ) 
    |         # OR ----------------------------- 
     (?>        # Start 'array(' 
      \b array \s* \(
    ) 
     (         # (2), Take off 'array(CORE-1)' 
      (?= .) 
      (?&core) 
     | 
    ) 
     \)         # End ')' 
    |         # OR ----------------------------- 
     \(        # Start '(' 
     (         # (3), Take off '(any CORE-2)' 
      (?= .) 
      (?&core) 
     | 
    ) 
     \)         # End ')' 
    |         # OR ----------------------------- 
     (         # (4), Take off Unbalanced or Exceptions 
      \b array \s* \(
     | [()] 
    ) 
) 

# Subroutines 
# --------------- 

(?(DEFINE) 

     # core 
     (?<core> 
      (?> 
       (?&content) 
      | 
       (?> \b array \s* \() 
       # recurse core of array() 
       (?: 
        (?= .) 
        (?&core) 
        | 
       ) 
       \) 
      | 
       \(
       # recurse core of any () 
       (?: 
        (?= .) 
        (?&core) 
        | 
       ) 
       \) 
      )+ 
    ) 

     # content 
     (?<content> 
      (?> 
       (?! 
        \b array \s* \(
        | [()] 
       ) 
       . 
      )+ 
    ) 
) 

Ausgabe

** Grp 0   - (pos 0 , len 11) 
some_var = 
** Grp 1   - (pos 0 , len 11) 
some_var = 
** Grp 2   - NULL 
** Grp 3   - NULL 
** Grp 4 [core] - NULL 
** Grp 5 [content] - NULL 

----------------------- 

** Grp 0   - (pos 11 , len 153) 
array(
    'id' => nextId(), 
    'profile' => array(
     'name' => 'Hugo Hurley', 
     'numbers' => (4 + 8 + 15 + 16 + 23 + 42)/108 
    ) 
) 
** Grp 1   - NULL 
** Grp 2   - (pos 17 , len 146) 

    'id' => nextId(), 
    'profile' => array(
     'name' => 'Hugo Hurley', 
     'numbers' => (4 + 8 + 15 + 16 + 23 + 42)/108 
    ) 

** Grp 3   - NULL 
** Grp 4 [core] - NULL 
** Grp 5 [content] - NULL 

------------------------------------- 

** Grp 0   - (pos 164 , len 3) 
; 

** Grp 1   - (pos 164 , len 3) 
; 

** Grp 2   - NULL 
** Grp 3   - NULL 
** Grp 4 [core] - NULL 
** Grp 5 [content] - NULL 

Eine frühere Inkarnation von etwas anderem, eine Vorstellung von Nutzung

# Perl code: 
# 
#  use strict; 
#  use warnings; 
#  
#  use Data::Dumper; 
#  
#  $/ = undef; 
#  my $content = <DATA>; 
#  
#  # Set the error mode on/off here .. 
#  my $BailOnError = 1; 
#  my $IsError = 0; 
#  
#  my $href = {}; 
#  
#  ParseCore($href, $content); 
#  
#  #print Dumper($href); 
#  
#  print "\n\n"; 
#  print "\nBase======================\n"; 
#  print $href->{content}; 
#  print "\nFirst======================\n"; 
#  print $href->{first}->{content}; 
#  print "\nSecond======================\n"; 
#  print $href->{first}->{second}->{content}; 
#  print "\nThird======================\n"; 
#  print $href->{first}->{second}->{third}->{content}; 
#  print "\nFourth======================\n"; 
#  print $href->{first}->{second}->{third}->{fourth}->{content}; 
#  print "\nFifth======================\n"; 
#  print $href->{first}->{second}->{third}->{fourth}->{fifth}->{content}; 
#  print "\nSix======================\n"; 
#  print $href->{six}->{content}; 
#  print "\nSeven======================\n"; 
#  print $href->{six}->{seven}->{content}; 
#  print "\nEight======================\n"; 
#  print $href->{six}->{seven}->{eight}->{content}; 
#  
#  exit; 
#  
#  
#  sub ParseCore 
#  { 
#   my ($aref, $core) = @_; 
#   my ($k, $v); 
#   while ($core =~ /(?is)(?:((?&content))|(?><!--block:(.*?)-->)((?&core)|)<!--endblock-->|(<!--(?:block:.*?|endblock)-->))(?(DEFINE)(?<core>(?>(?&content)|(?><!--block:.*?-->)(?:(?&core)|)<!--endblock-->)+)(?<content>(?>(?!<!--(?:block:.*?|endblock)-->).)+))/g) 
#   { 
#   if (defined $1) 
#   { 
#    # CONTENT 
#    $aref->{content} .= $1; 
#   } 
#   elsif (defined $2) 
#   { 
#    # CORE 
#    $k = $2; $v = $3; 
#    $aref->{$k} = {}; 
#  #   $aref->{$k}->{content} = $v; 
#  #   $aref->{$k}->{match} = $&; 
#     
#    my $curraref = $aref->{$k}; 
#    my $ret = ParseCore($aref->{$k}, $v); 
#    if ($BailOnError && $IsError) { 
#     last; 
#    } 
#    if (defined $ret) { 
#     $curraref->{'#next'} = $ret; 
#    } 
#   } 
#   else 
#   { 
#    # ERRORS 
#    print "Unbalanced '$4' at position = ", $-[0]; 
#    $IsError = 1; 
#  
#    # Decide to continue here .. 
#    # If BailOnError is set, just unwind recursion. 
#    # ------------------------------------------------- 
#    if ($BailOnError) { 
#     last; 
#    } 
#   } 
#   } 
#   return $k; 
#  } 
#  
#  #================================================ 
#  __DATA__ 
#  some html content here top base 
#  <!--block:first--> 
#   <table border="1" style="color:red;"> 
#   <tr class="lines"> 
#    <td align="left" valign="<--valign-->"> 
#   <b>bold</b><a href="http://www.mewsoft.com">mewsoft</a> 
#   <!--hello--> <--again--><!--world--> 
#   some html content here 1 top 
#   <!--block:second--> 
#    some html content here 2 top 
#    <!--block:third--> 
#     some html content here 3 top 
#     <!--block:fourth--> 
#      some html content here 4 top 
#      <!--block:fifth--> 
#       some html content here 5a 
#       some html content here 5b 
#      <!--endblock--> 
#     <!--endblock--> 
#     some html content here 3a 
#     some html content here 3b 
#    <!--endblock--> 
#    some html content here 2 bottom 
#   <!--endblock--> 
#   some html content here 1 bottom 
#  <!--endblock--> 
#  some html content here1-5 bottom base 
#  
#  some html content here 6-8 top base 
#  <!--block:six--> 
#   some html content here 6 top 
#   <!--block:seven--> 
#    some html content here 7 top 
#    <!--block:eight--> 
#     some html content here 8a 
#     some html content here 8b 
#    <!--endblock--> 
#    some html content here 7 bottom 
#   <!--endblock--> 
#   some html content here 6 bottom 
#  <!--endblock--> 
#  some html content here 6-8 bottom base 
# 
# Output >> 
# 
#  Base====================== 
#  some html content here top base 
#  
#  some html content here1-5 bottom base 
#  
#  some html content here 6-8 top base 
#  
#  some html content here 6-8 bottom base 
#  
#  First====================== 
#  
#   <table border="1" style="color:red;"> 
#   <tr class="lines"> 
#    <td align="left" valign="<--valign-->"> 
#   <b>bold</b><a href="http://www.mewsoft.com">mewsoft</a> 
#   <!--hello--> <--again--><!--world--> 
#   some html content here 1 top 
#   
#   some html content here 1 bottom 
#  
#  Second====================== 
#  
#    some html content here 2 top 
#    
#    some html content here 2 bottom 
#   
#  Third====================== 
#  
#     some html content here 3 top 
#     
#     some html content here 3a 
#     some html content here 3b 
#    
#  Fourth====================== 
#  
#      some html content here 4 top 
#      
#     
#  Fifth====================== 
#  
#       some html content here 5a 
#       some html content here 5b 
#      
#  Six====================== 
#  
#   some html content here 6 top 
#   
#   some html content here 6 bottom 
#  
#  Seven====================== 
#  
#    some html content here 7 top 
#    
#    some html content here 7 bottom 
#   
#  Eight====================== 
#  
#     some html content here 8a 
#     some html content here 8b 
#   
+0

Es gibt aber noch viel mehr dazu, es ist zum Beispiel für dumme Zwecke gedacht. Einen richtigen Parser in einer Regex-Lösung zu erstellen, ist eine große Aufgabe, nicht für das schwache Herz. – sln

2

Wie über die folgenden (mit .NET Regex-Engine) verwendet lösen:

resultString = Regex.Replace(subjectString, 
    @"\barray\(   # Match 'array(' 
    (      # Capture in group 1: 
    (?>     # Start a possessive group: 
     (?:     # Either match 
     (?!\barray\(|[()]) # only if we're not before another array or parens 
     .     # any character 
    )+     # once or more 
    |      # or 
     \((?<Depth>)  # match '(' (and increase the nesting counter) 
    |      # or 
     \) (?<-Depth>)  # match ')' (and decrease the nesting counter). 
    )*     # Repeat as needed. 
    (?(Depth)(?!))  # Assert that the nesting counter is at zero. 
    )      # End of capturing group. 
    \)      # Then match ')'.", 
    "[$1]", RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline); 

Dieser Regex stimmt mit array(...) überein, wobei ... alles außer einem anderen array(...) enthalten kann (es passt also nur zu den am tiefsten verschachtelten Vorkommen). Es erlaubt andere verschachtelte (und korrekt abgeglichene) Klammern innerhalb der ..., aber es überprüft nicht, ob das semantische Klammern sind oder ob sie in Strings oder Kommentaren enthalten sind.

Mit anderen Worten, so etwas wie

array(
    'name' => 'Hugo (((Hurley', 
    'numbers' => (4 + 8 + 15 + 16 + 23 + 42)/108 
) 

scheitern würde (richtig) entsprechen.

Sie müssen diese Regex iterativ anwenden, bis sie ihre Eingabe nicht mehr ändert - im Beispiel würden zwei Iterationen ausreichen.