Регулярное выражение для получения текста в скобках - PullRequest
2 голосов
/ 20 февраля 2009

Я смотрю на строку и пытаюсь получить все внутри пары скобок. Содержимое может измениться, а максимальные и минимальные значения могут отсутствовать при определенных обстоятельствах.

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

Там, где where () и sort () не гарантированы.
Между каждым набором могут быть пробелы, и [ПРАВИТЬ] ключевые слова могут не всегда совпадать.

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

Какой шаблон регулярного выражения следует использовать? По сути, он должен вернуть:

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

Я понимаю, что изменение первого набора скобок на {или [может решить проблему, но я упрям ​​и хочу сделать это таким образом с помощью регулярных выражений.

EDIT Лучшее, что я мог придумать, используя preg_match_all ()

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

Ответы [ 6 ]

4 голосов
/ 20 февраля 2009

Вам лучше использовать такой парсер, как:

$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);
1 голос
/ 20 февраля 2009

Поскольку вы пояснили, что это необязательно, я не верю, что это будет возможно сделать с помощью регулярного выражения. Вы могли бы сделать это, сохранив различные предложения (get, where, sort) в своих собственных строках, но я не думаю, что вы сможете сделать это как есть.

Снова отредактируйте: концептуально это похоже на вчерашний вопрос, который, как было показано, невозможно сделать с помощью регулярного выражения: Regex для проверки, если строка не соответствует круглым скобкам?

0 голосов
/ 20 февраля 2009

Я немного посидел и написал радикальный парсер FSM, просто ради интереса.

У него есть несколько функций, которые вы вряд ли увидите в regex (по крайней мере, в PHP, я мог бы сделать это с рекурсивным регулярным выражением в Perl, но не в PHP, у него пока нет этой функции).

  1. Интеллектуальный и основанный на стеке анализ скобок
  2. Поддержка AnyBracket
  3. Модульная
  4. Extensible.
  5. Если синтаксис неправильный, он может сказать вам, где что-то.

Конечно, здесь есть куча кода, и большая его часть немного странна и запутанна для новых программистов, но с точки зрения того, что это, довольно классная штука.

Это не готовый продукт, просто что-то, что я бросил вместе, но он работает и не имеет никаких ошибок, которые я могу найти.

У меня есть «умри» во многих местах, где обычно было бы лучше использовать исключения и все такое, поэтому очистка и рефакторинг предпочтительнее, прежде чем просто развернуть его.

У него есть достаточное количество комментариев, но я чувствую, что если бы я прокомментировал дальше, то сложнее понять, что такое обработка конечных состояний.



# 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);

Все вышеперечисленные строки print_r ($ structure) должны создать это:

Array
(
    [get] => max(fieldname1),min(fieldname2),fieldname3
    [where] => something=something
    [sort] => fieldname2 asc
    [_Trailing_Data] =>
)
0 голосов
/ 20 февраля 2009

Это очень хакерский способ сделать это, возможно, можно было бы сделать лучше, но только как подтверждение концепции:

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

Местоположение данных будет изменяться в массиве совпадений в зависимости от того, найдена информация или нет. Вы можете проверить это там !

0 голосов
/ 20 февраля 2009

Я поддерживаю сказанное о том, что регулярное выражение не подходит для общей структуры, подобной этой. Тем не менее, при условии, что круглые скобки сбалансированы и содержат не более двух глубин, эти регулярные выражения могут помочь:

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

соответствует и захватывает один экземпляр xyz (....), а

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

соответствует всем им. В зависимости от вашего языка, вы можете использовать второй и разделить несколько снимков в одной группе. Эта ссылка может быть полезна.

Но, повторюсь, я не думаю, что регулярное выражение - это путь - вот почему это довольно ограничительное решение настолько неприятно.


Извините, только что заметил, что вы PHP. Вам, вероятно, понадобится использовать это:

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

, чтобы разделить вашу линию на (цельный) плюс (остаток) и перебирать, пока не останется ничего.

0 голосов
/ 20 февраля 2009

А как же?

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

Теперь я не уверен, что это сработает. Например, первое совпадение (для get) может быть переполнено в предложениях where и sort. Вы могли бы иметь возможность справиться с этим, используя взгляды, например:

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

но на самом деле это довольно грубое регулярное выражение, и Gumbo прав в том, что синтаксический анализатор, возможно, лучший путь. Это относится ко всему, где у вас есть соответствующие элементы. HTML / XML - это классический случай, когда регулярное выражение используется не по назначению и часто. В этих случаях хуже, потому что парсеры свободно доступны и зрелы.

Есть много случаев, в которых нужно разобраться, например:

  • Необязательность частей выражения;
  • Ложные сигналы от литералов, например, get (") sort") нарушат вышеприведенное;
  • побег персонажей;
  • вложенности.

Чад указывает на проблему с подходящей парой, о которой я говорил, и стоит повторить. Допустим, у вас есть следующий HTML:

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

Получение сопоставленных пар тегов невозможно с помощью регулярного выражения (но люди продолжают пытаться или просто не учитывают тип ввода). Что делает ваш случай возможно работоспособным, так это то, что у вас есть несколько известных маркеров, которые вы можете использовать:

  • Ключевые слова получить, где и сортировать; и
  • Начало и конец строки.

Но, честно говоря, регулярное выражение не рекомендуется.

Так что, если вы хотите что-то надежное и надежное, напишите парсер. Regex для такого рода вещей - не более чем быстрое и грязное решение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...