Parantez içindeki metni almak için düzenli ifade

6 Cevap php

I'm looking at a string and trying to get everything inside the pair of brackets. The contents may change and the max and min may not exist in certain cirumstances.

get(max(fieldname1),min(fieldname2),fieldname3)where(something=something) sort(fieldname2 asc)

The where() and sort() are not guaranteed to be there.
There may be spaces between each set and [EDIT] the keywords may not always be the same.

get(something) where(something)
get(something)where(something) sort(something)

What regex pattern should be used? Effectively, it should return:

Array (
[0] => max(fieldname1),min(fieldname2),fieldname3
[1] => something=something
[2] => fieldname2 asc
)

Ben var [{ya da parantez ilk seti değiştirerek sorunu çözebilir biliyoruz ama ben inatçı değilim ve regex tarafından bu şekilde yapmak istiyorum.

EDIT The best I could come up with using preg_match_all()

/[a-zA-Z0-9_]+\((.*?)\)/

6 Cevap

: Daha iyi gibi bir ayrıştırıcı kullanmak

$str = 'get(max(fieldname1),min(fieldname2),fieldname3)where(something=something) sort(fieldname2 asc)';
$array = array();
$buffer = '';
$depth = 0;
for ($i=0; $i<strlen($str); $i++) {
    $buffer .= $str[$i];
    switch ($str[$i]) {
        case '(':
            $depth++;
            break;
        case ')':
            $depth--;
            if ($depth === 0) {
                $array[] = $buffer;
                $buffer = '';
            }
            break;
    }
}
var_dump($array);

Bu isteğe bağlı olduğunu açıkladı beri, ben bu bir düzenli ifade ile yapmak mümkün olacaktır inanmıyorum. Sen kendi dizeleri (burada, çeşit olsun) farklı hükümler tutarak mümkün yapabilir, ama olduğu gibi-bunu yapmak mümkün olacak sanmıyorum.

Edit again: It's conceptually somewhat similar to this question from yesterday, which was shown to be impossible to do with a regex: Regex for checking if a string has mismatched parentheses?

Ne hakkında?

^\s*get\((.*?)\)(?:\s*where\((.*?)\))(?:\s*sort\((.*?)\)\s*)?$

şimdi ben bu işe ikna edecek değilim. Örneğin (get için) ilk maçında yerde içine taşması ve hükümler sıralayabilirsiniz. Sen might, örneğin lookaheads kullanarak bu ile anlaşma mümkün:

^\s*get\(((?:.(?!sort|where))*?)\)(?:\s*where\(((?:.(?!sort))*?)\))(?:\s*sort\((.*?)\)\s*)?$

ama gerçekten bu bir yamuk yumuk düzenli ifade ve bamya bir ayrıştırıcı belki gitmek için iyi bir yoldur doğru bu değildir. Bu unsurları eşleşmiş şey için böyledir. HTML / XML regex uygunsuz ve sık kullanılan nerede için klasik bir durum vardır. Ayrıştırıcılar serbestçe kullanılabilir ve olgun, çünkü bu durumlarda daha kötü.

Böyle bir şey başa durumlarda çok vardır:

  • İfadenin parçaları optionality;
  • Değişmezleriyle sahte sinyaller örneğin olsun (") sort") Yukarıdaki kıracak;
  • Karakter kaçan;
  • Yuvalama.

Chad bahsettiğim eşleşen çifti sorunu işaret ve reitering değer. Aşağıdaki HTML söylüyorlar:

<div>
  <div></div>
</div>

Etiketleri eşleşen çiftleri alma regex imkansız (henüz insanlar çalışırken ya da sadece giriş türü için muhasebe değil tutun). Ne durumda yapar possibly uygulanabilir size kullanabileceğiniz bazı belirteç bilinir olması:

  • Anahtar kelimeler, nerede ve sıralama olsun; ve
  • Dize başlangıç ​​ve bitiş.

Ama dürüst olmak gerekirse regex önerilen yaklaşım değildir.

Yani sağlam ve güvenilir bir şey istiyorsanız, bir ayrıştırıcı yazmak. Bu tür bir şey için Regex hızlı ve kirli bir çözüm başka bir şey değildir.

Ben bir sıradanifade böyle bir genel yapısına uygun değil varlık hakkında söylenenleri destekler. Bununla birlikte, parantez dengeli ve ikiden fazla derin şu regexes yardımcı olabilir olmaması şartıyla:

(\w+\s*\([^()]*(?:(?:\([^()]*\))[^()]*)*\)\s*)

karşılaşmalar ve tek xyz (....) örneği ise yakalar

(\w+\s*\([^()]*(?:(?:\([^()]*\))[^()]*)*\)\s*)+

hepsi eşleşir. Dilinize bağlı olarak, ikinci birini kullanın ve tek bir grup halinde birkaç yakalar çözmek mümkün olabilir. This reference yararlı olabilir.

Ancak, tekrarlamak, ben regex yol olduğunu düşünmüyorum - bu oldukça kısıtlayıcı çözüm o kadar çirkin neden budur.


Üzgünüm, sadece PHP olduğunuzu kaydetti. Muhtemelen bu kullanmanız gerekir:

(\w+\s*\([^()]*(?:(?:\([^()]*\))[^()]*)*\)\s*)(.*)

şey kalmadı etrafında kadar (tek parça) artı (-dinlenme) ve döngü içine hattı bölmek için.

Bu bunu yapmanın çok hackish yoludur, muhtemelen daha iyi yapılır, ama sadece kavramının kanıtı olarak olabilir:

get\((max\(.+?\)),(min\(.+?\)),(.+?)\)(where\((.+?=.+?)\)| where\((.+?=.+?)\)|)(sort\((.+?)\)| sort\((.+?)\)|)

Data location will change in match array depending whether or not information is found. You can test it out there!

Ben bir süre oturdu ve sadece ilgi uğruna, tam tüylenmek FSM ayrıştırıcı yazdı.

Bu regex ile görmek olası değil bir kaç özellikleri vardır (en azından PHP altında, Perl özyinelemeli regex ile yapabilirdi, ama PHP, henüz bu özelliği yok).

  1. Akıllı ve yığın tabanlı dirsek ayrıştırma
  2. AnyBracket Destek
  3. Modüler
  4. Genişletilebilir.
  5. Sözdizimi yanlış olduğunda, nerede biraz söyleyebilirim.

Verilen orada bir kod butload burada ve bunun bir sürü biraz bizzare ve yeni kodlayıcılar da kıvrık olduğunu, ancak, onun oldukça harika şeyler ne açısından.

Onun değil bir bitmiş ürün, sadece Someting ben birlikte attı, ancak çalışıyor ve ben bulabilirim herhangi bir hata yok.

Ben Normalde İstisnalar ve etajer kullanmak daha iyi olurdu yerlerde bir sürü 'die' var, yani bir temizleme ve Refactor sadece yayıyoruz önce tercih olacaktır.

Bu yorumlama makul bir miktar var, ama sonlu-durum-işleme anlamak zor olurdu ayrıca asıl mesele comment olsaydı ben hissediyorum.



# Pretty Colour Debug of the tokeniser in action. 
# Uncomment to use. 
function debug( $title, $stream, $msg, $remaining ){ 
#  print chr(27) ."[31m$title" . chr(27) ."[0m\n";
# print chr(27) ."[33min:$stream" . chr(27) ."[0m\n";
#  print chr(27) ."[32m$msg" . chr(27) ."[0m\n";
#  print chr(27) ."[34mstream:$remaining" . chr(27) ."[0m\n\n";
}

# Simple utility to store a captured part of the stream in one place
# and the remainder somewhere else
# Wraps most the regexy stuff 
# Insprired by some Perl Regex Parser I found. 

function get_token( $regex, $input ){ 
  $out = array( 
      'success' => false,
      'match' => '',
      'rest' => ''
  );
  if( !preg_match( '/^' . $regex . '/' , $input, $matches ) ){
    die("Could not match $regex at start of $input ");
    #return $out; # error condition, not matched. 
  }
  $out['match'] = $matches[1];
  $out['rest'] = substr( $input, strlen( $out['match'] ) );
  $out['success'] = true;
  debug( 'Scan For Token: '. $regex , $input, "matched: " . $out['match'] , $out['rest'] );
  return $out;
}


function skip_space( $input ){ 
  return get_token('(\s*)', $input ); 
}

# Given $input and $opener, find 
# the data stream that occurs until the respecive closer. 
# All nested bracket sets must be well balanced. 
# No 'escape code' implementation has been done (yet) 
# Match will contain the contents, 
# Rest will contain unprocessed part of the string
# []{}() and  bracket types are currently supported. 

function close_bracket( $input , $opener ){
  $out = array( 
      'success' => false,
      'match' => '',
      'rest' => ''
  );

  $map = array( '(' => ')', '[' => ']', '{' => '}', chr(60) => '>' );
  $nests = array( $map[$opener] ); 

  while( strlen($input) > 0 ){ 
    $d = get_token( '([^()\[\]{}' . chr(60). '>]*?[()\[\]{}' . chr(60)  . '>])', $input ); 
    $input = $d['rest']; 

    if( !$d['success'] ){  
      debug( 'Scan For ) Bailing ' , $input, "depth: $nests, matched: " . $out['match'] , $out['rest'] );

      $out['match'] .= $d['match'];
      return $out; # error condition, not matched. brackets are imbalanced. 
    }

# Work out which of the 4 bracket types we got, and
# Which orientation it is, and then decide if were going up the tree or down it

    end($nests);
    $tail = substr( $d['match'], -1, 1 );
    if( $tail == current($nests) ){ 
      array_pop( $nests );
    } elseif ( array_key_exists( $tail, $map ) ){ 
      array_push( $nests, $map[$tail] ); 
    } else {
      die ("Error. Bad bracket Matching, unclosed/unbalanced/unmatching bracket sequence: " . $out['match'] . $d['match'] );
    }
    $out['match'] .= $d['match'] ; 
    $out['rest' ]  = $d['rest'];
    debug( 'Scan For ) running' , $input, "depth: $nests, matched: " . $out['match'] , $out['rest'] );

    if ( count($nests) == 0 ){ 
      # Chomp off the tail bracket to just get the body
      $out['match'] = substr( $out['match'] , 0 , -1 );
      $out['success'] = true;
      debug( 'Scan For ) returning ' , $input, "matched: " . $out['match'] , $out['rest'] );
      return $out;
    }
    else { 

    }
  }
  die('Scan for closing ) exhausted buffer while searching. Brackets Missmatched. Fix this: \'' . $out['match'] . '\'');
}

# Given $function_name and $input, expects the form fnname(data) 
# 'data' can be any well balanced bracket sequence 
# also, brackets used for functions in the stream can be any of your choice, 
# as long as you're consistent. fnname[foo] will work. 

function parse_function_body( $input, $function_name ){ 
  $out = array ( 
    'success' => false, 
    'match' => '', 
    'rest' => '', 
  );

  debug( 'Parsing  ' . $function_name . "()", $input, "" , "" );

  $d = get_token( "(" . $function_name . '[({\[' . chr(60) . '])' , $input ); 

  if ( !$d['success'] ){ 
     die("Doom while parsing for function $function_name. Not Where its expected.");
  }

  $e = close_bracket( $d['rest'] , substr($d['match'],-1,1) );

  if ( !$e['success'] ){
    die("Found Imbalanced Brackets while parsing for $function_name, last snapshot was '" . $e['match'] . "'");
    return $out; # inbalanced brackets for function
  }
  $out['success'] = true;
  $out['match'] = $e['match']; 
  $out['rest'] = $e['rest'];
  debug( 'Finished Parsing  ' . $function_name . "()", $input, 'body:'. $out['match'] , $out['rest'] );

  return $out;
}

function  parse_query( $input ){ 

  $eat  = skip_space( $input ); 
  $get = parse_function_body( $eat['rest'] , 'get' ); 
  if ( !$get['success'] ){ 
    die("Get Token Malformed/Missing, instead found '" . $eat['rest'] . "'"); 
  }
  $eat = skip_space( $get['rest'] ); 
  $where = parse_function_body( $eat['rest'], 'where' ); 
  if ( !$where['success'] ){ 
    die("Where Token Malformed/Missing, instead found '" . $eat['rest'] . "'"); 
  }
  $eat = skip_space( $where['rest'] ); 
  $sort = parse_function_body( $eat['rest'], 'sort' ); 
  if( !$sort['success'] ){
    die("Sort Token Malformed/Missing, instead found '" . $eat['rest'] . "'"); 
  }
  return array( 
      'get' => $get['match'],
      'where' => $where['match'], 
      'sort' => $sort['match'], 
      '_Trailing_Data' =>  $sort['rest'],
  );
}



$structure = parse_query("get[max(fieldname1),min(fieldname2),fieldname3]where(something=something) sort(fieldname2 asc)");

print_r($structure);

$structure = parse_query("get(max(fieldname1),min(fieldname2),fieldname3)where(something=something) sort(fieldname2 asc)");

print_r($structure);

$structure = parse_query("get{max(fieldname1),min(fieldname2),fieldname3}where(something=something) sort(fieldname2 asc)");

print_r($structure);

$structure = parse_query("get" . chr(60) . "max(fieldname1),min(fieldname2),fieldname3" . chr(60). "where(something=something) sort(fieldname2 asc)");

print_r($structure);
All of the above print_r($structure) lines should produce this:
Array
(
    [get] => max(fieldname1),min(fieldname2),fieldname3
    [where] => something=something
    [sort] => fieldname2 asc
    [_Trailing_Data] =>
)