Объединение регулярных выражений в PHP

Это не возможно легким способом. Столбец наследуется, и если вы не хотите наследовать некоторые из столбцов, вам нужно объявить таблицу, которая не наследует эти столбцы.

Возможно, вам нужно больше абстрактных таблиц для наследования, прежде чем те, которые ваша таблица наследует в настоящее время.

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

  1. создать больше абстрактных таблиц для наследования (при необходимости) только с необходимыми столбцами
  2. [ 111] создать таблицу с пропущенными столбцами для ее наследования
  3. скопировать данные из необходимых столбцов в эту новую таблицу
  4. скопировать данные, исключая ненужные столбцы, в другую таблицу, которая не наследует таблицы с нежелательными колонны
10
задан Konrad Rudolph 28 October 2008 в 22:03
поделиться

6 ответов

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

Одна слабость - это, не обрабатывает обратные ссылки в рамках выражений. Мое самое большое беспокойство с этим является ограничениями самих обратных ссылок. Я оставлю это как осуществление читателю/корреспонденту.

// Pass as many expressions as you'd like
function preg_magic_coalesce() {
    $active_modifiers = array();

    $expression = '/(?:';
    $sub_expressions = array();
    foreach(func_get_args() as $arg) {
        // Determine modifiers from sub-expression
        if(preg_match('/^(.)(.*)\1([eimsuxADJSUX]+)$/', $arg, $matches)) {
            $modifiers = preg_split('//', $matches[3]);
            if($modifiers[0] == '') {
                array_shift($modifiers);
            }
            if($modifiers[(count($modifiers) - 1)] == '') {
                array_pop($modifiers);
            }

            $cancel_modifiers = $active_modifiers;
            foreach($cancel_modifiers as $key => $modifier) {
                if(in_array($modifier, $modifiers)) {
                    unset($cancel_modifiers[$key]);
                }
            }
            $active_modifiers = $modifiers;
        } elseif(preg_match('/(.)(.*)\1$/', $arg)) {
            $cancel_modifiers = $active_modifiers;
            $active_modifiers = array();
        }

        // If expression has modifiers, include them in sub-expression
        $sub_modifier = '(?';
        $sub_modifier .= implode('', $active_modifiers);

        // Cancel modifiers from preceding sub-expression
        if(count($cancel_modifiers) > 0) {
            $sub_modifier .= '-' . implode('-', $cancel_modifiers);
        }

        $sub_modifier .= ')';

        $sub_expression = preg_replace('/^(.)(.*)\1[eimsuxADJSUX]*$/', $sub_modifier . '$2', $arg);

        // Properly escape slashes
        $sub_expression = preg_replace('/(?<!\\\)\//', '\\\/', $sub_expression);

        $sub_expressions[] = $sub_expression;
    }

    // Join expressions
    $expression .= implode('|', $sub_expressions);

    $expression .= ')/';
    return $expression;
}

Править: Я переписал это (потому что я - OCD), и закончился с:

function preg_magic_coalesce($expressions = array(), $global_modifier = '') {
    if(!preg_match('/^((?:-?[eimsuxADJSUX])+)$/', $global_modifier)) {
        $global_modifier = '';
    }

    $expression = '/(?:';
    $sub_expressions = array();
    foreach($expressions as $sub_expression) {
        $active_modifiers = array();
        // Determine modifiers from sub-expression
        if(preg_match('/^(.)(.*)\1((?:-?[eimsuxADJSUX])+)$/', $sub_expression, $matches)) {
            $active_modifiers = preg_split('/(-?[eimsuxADJSUX])/',
                $matches[3], -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
        }

        // If expression has modifiers, include them in sub-expression
        if(count($active_modifiers) > 0) {
            $replacement = '(?';
            $replacement .= implode('', $active_modifiers);
            $replacement .= ':$2)';
        } else {
            $replacement = '$2';
        }

        $sub_expression = preg_replace('/^(.)(.*)\1(?:(?:-?[eimsuxADJSUX])*)$/',
            $replacement, $sub_expression);

        // Properly escape slashes if another delimiter was used
        $sub_expression = preg_replace('/(?<!\\\)\//', '\\\/', $sub_expression);

        $sub_expressions[] = $sub_expression;
    }

    // Join expressions
    $expression .= implode('|', $sub_expressions);

    $expression .= ')/' . $global_modifier;
    return $expression;
}

Это теперь использует (?modifiers:sub-expression) вместо (?modifiers)sub-expression|(?cancel-modifiers)sub-expression но я заметил, что у обоих есть некоторые странные побочные эффекты модификатора. Например, в обоих случаях если подвыражение имеет a /u модификатор, это не будет соответствовать (но если Вы передаете 'u' как второй аргумент новой функции, которая будет соответствовать очень хорошо).

3
ответ дан 4 December 2019 в 01:32
поделиться
  1. Разделители полосы и флаги от каждого. Этот regex должен сделать это:

    /^(.)(.*)\1([imsxeADSUXJu]*)$/
    
  2. Выражения соединения вместе. Вам будет нужна круглая скобка неполучения для введения флагов:

    "(?$flags1:$regexp1)|(?$flags2:$regexp2)"
    
  3. Если существуют какие-либо обратные ссылки, круглая скобка получения количества и обновляют обратные ссылки соответственно (например, правильно присоединенный /(.)x\1/ и /(.)y\1/ /(.)x\1|(.)y\2/ ).

3
ответ дан 4 December 2019 в 01:32
поделиться

Править

Я переписал код! Это теперь содержит изменения, которые перечислены следующим образом. Кроме того, я сделал обширные тесты (который я не отправлю здесь, потому что они - слишком многие) искать ошибки. До сих пор я не нашел никого.

  • Функция теперь разделяется на две части: существует отдельная функция preg_split который берет регулярное выражение и возвращает массив, содержащий пустое выражение (без разделителей) и массив модификаторов. Это могло бы пригодиться (это уже имеет на самом деле; это - то, почему я внес это изменение).

  • Код теперь правильно обрабатывает обратные ссылки. Это было необходимо для моей цели, в конце концов. Не было трудно добавить, регулярное выражение, используемое для получения обратных ссылок просто, выглядит странным (и может на самом деле быть чрезвычайно неэффективным, это выглядит NP-трудным мне – но это - только интуиция и только применяется в странных пограничных случаях). Между прочим, кто-либо знает лучший способ проверить на неровное количество соответствий, чем мой путь? Отрицательный lookbehinds не будет работать здесь, потому что они только принимают строки фиксированной длины вместо регулярных выражений. Однако мне нужен regex здесь, чтобы протестировать, оставляют ли предыдущей обратной косой черты на самом деле самой.

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

  • Я исправил ошибку в проверке работоспособности.

  • Я удалил отмену устаревших модификаторов, так как мои тесты показывают, что это не необходимо.

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

Спасибо!

porneL, eyelidlessness, удивительная работа! Многие, большое спасибо. Я на самом деле сдался.

Я положился на Ваше решение, и я хотел бы совместно использовать его здесь. Я не реализовал обратные ссылки изменения нумерации, так как это не релевантно в моем случае (я думаю …). Возможно, это станет необходимым позже, все же.

Некоторые вопросы …

Одна вещь, @eyelidlessness: Почему Вы чувствуете необходимость отменить старые модификаторы? Насколько я вижу его, это не необходимо, так как модификаторы только применяются локально так или иначе. А-ч да, еще одна вещь. Ваш выход разделителя кажется чрезмерно сложным. Хотите объяснить, почему Вы думаете, что это необходимо? Я полагаю, что моя версия должна работать также, но я мог быть очень неправым.

Кроме того, я изменил подпись Вашей функции для соответствия моим потребностям. Я также вещь, что моя версия в более общем плане полезна. Снова, я мог бы быть неправым.

BTW, необходимо теперь понять важность настоящих имен на ТАК.;-) Я не могу дать Вам реальный кредит в коде.:-/

Код

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

И без дальнейшей суматохи …

/**
 * Merges several regular expressions into one, using the indicated 'glue'.
 *
 * This function takes care of individual modifiers so it's safe to use
 * <em>different</em> modifiers on the individual expressions. The order of
 * sub-matches is preserved as well. Numbered back-references are adapted to
 * the new overall sub-match count. This means that it's safe to use numbered
 * back-refences in the individual expressions!
 * If {@link $names} is given, the individual expressions are captured in
 * named sub-matches using the contents of that array as names.
 * Matching pair-delimiters (e.g. <code>"{…}"</code>) are currently
 * <strong>not</strong> supported.
 *
 * The function assumes that all regular expressions are well-formed.
 * Behaviour is undefined if they aren't.
 *
 * This function was created after a {@link https://stackoverflow.com/questions/244959/
 * StackOverflow discussion}. Much of it was written or thought of by
 * “porneL” and “eyelidlessness”. Many thanks to both of them.
 *
 * @param string $glue  A string to insert between the individual expressions.
 *      This should usually be either the empty string, indicating
 *      concatenation, or the pipe (<code>|</code>), indicating alternation.
 *      Notice that this string might have to be escaped since it is treated
 *      like a normal character in a regular expression (i.e. <code>/</code>)
 *      will end the expression and result in an invalid output.
 * @param array $expressions    The expressions to merge. The expressions may
 *      have arbitrary different delimiters and modifiers.
 * @param array $names  Optional. This is either an empty array or an array of
 *      strings of the same length as {@link $expressions}. In that case,
 *      the strings of this array are used to create named sub-matches for the
 *      expressions.
 * @return string An string representing a regular expression equivalent to the
 *      merged expressions. Returns <code>FALSE</code> if an error occurred.
 */
function preg_merge($glue, array $expressions, array $names = array()) {
    // … then, a miracle occurs.

    // Sanity check …

    $use_names = ($names !== null and count($names) !== 0);

    if (
        $use_names and count($names) !== count($expressions) or
        !is_string($glue)
    )
        return false;

    $result = array();
    // For keeping track of the names for sub-matches.
    $names_count = 0;
    // For keeping track of *all* captures to re-adjust backreferences.
    $capture_count = 0;

    foreach ($expressions as $expression) {
        if ($use_names)
            $name = str_replace(' ', '_', $names[$names_count++]);

        // Get delimiters and modifiers:

        $stripped = preg_strip($expression);

        if ($stripped === false)
            return false;

        list($sub_expr, $modifiers) = $stripped;

        // Re-adjust backreferences:

        // We assume that the expression is correct and therefore don't check
        // for matching parentheses.

        $number_of_captures = preg_match_all('/\([^?]|\(\?[^:]/', $sub_expr, $_);

        if ($number_of_captures === false)
            return false;

        if ($number_of_captures > 0) {
            // NB: This looks NP-hard. Consider replacing.
            $backref_expr = '/
                (                # Only match when not escaped:
                    [^\\\\]      # guarantee an even number of backslashes
                    (\\\\*?)\\2  # (twice n, preceded by something else).
                )
                \\\\ (\d)        # Backslash followed by a digit.
            /x';
            $sub_expr = preg_replace_callback(
                $backref_expr,
                create_function(
                    '$m',
                    'return $m[1] . "\\\\" . ((int)$m[3] + ' . $capture_count . ');'
                ),
                $sub_expr
            );
            $capture_count += $number_of_captures;
        }

        // Last, construct the new sub-match:

        $modifiers = implode('', $modifiers);
        $sub_modifiers = "(?$modifiers)";
        if ($sub_modifiers === '(?)')
            $sub_modifiers = '';

        $sub_name = $use_names ? "?<$name>" : '?:';
        $new_expr = "($sub_name$sub_modifiers$sub_expr)";
        $result[] = $new_expr;
    }

    return '/' . implode($glue, $result) . '/';
}

/**
 * Strips a regular expression string off its delimiters and modifiers.
 * Additionally, normalize the delimiters (i.e. reformat the pattern so that
 * it could have used '/' as delimiter).
 *
 * @param string $expression The regular expression string to strip.
 * @return array An array whose first entry is the expression itself, the
 *      second an array of delimiters. If the argument is not a valid regular
 *      expression, returns <code>FALSE</code>.
 *
 */
function preg_strip($expression) {
    if (preg_match('/^(.)(.*)\\1([imsxeADSUXJu]*)$/s', $expression, $matches) !== 1)
        return false;

    $delim = $matches[1];
    $sub_expr = $matches[2];
    if ($delim !== '/') {
        // Replace occurrences by the escaped delimiter by its unescaped
        // version and escape new delimiter.
        $sub_expr = str_replace("\\$delim", $delim, $sub_expr);
        $sub_expr = str_replace('/', '\\/', $sub_expr);
    }
    $modifiers = $matches[3] === '' ? array() : str_split(trim($matches[3]));

    return array($sub_expr, $modifiers);
}

PS: я сделал эту отправляющую общественную Wiki доступной для редактирования. Вы знаете то, что это означает …!

3
ответ дан 4 December 2019 в 01:32
поделиться

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

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

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

1
ответ дан 4 December 2019 в 01:32
поделиться
function preg_magic_coalasce($split, $re1, $re2) {
  $re1 = rtrim($re1, "\/#is");
  $re2 = ltrim($re2, "\/#");
  return $re1.$split.$re2;
}
0
ответ дан 4 December 2019 в 01:32
поделиться

Вы могли сделать это альтернативный путь как это:

$a = '# /[a-z] #i';
$b = '/ Moo /x';

$a_matched = preg_match($a, $text, $a_matches);
$b_matched = preg_match($b, $text, $b_matches);

if ($a_matched && $b_matched) {
    $a_pos = strpos($text, $a_matches[1]);
    $b_pos = strpos($text, $b_matches[1]);

    if ($a_pos == $b_pos) {
        if (strlen($a_matches[1]) == strlen($b_matches[1])) {
            // $a and $b matched the exact same string
        } else if (strlen($a_matches[1]) > strlen($b_matches[1])) {
            // $a and $b started matching at the same spot but $a is longer
        } else {
            // $a and $b started matching at the same spot but $b is longer
        }
    } else if ($a_pos < $b_pos) {
        // $a matched first
    } else {
        // $b matched first
    }
} else if ($a_matched) {
    // $a matched, $b didn't
} else if ($b_matched) {
    // $b matched, $a didn't
} else {
    // neither one matched
}
0
ответ дан 4 December 2019 в 01:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: