Мне нужно определить, содержит ли строка какие-либо незамкнутые угловые скобки.
Я попытался избежать регулярного выражения путем сравнения числа левого и правого скобок:
if (substr_count($string, '<') !== substr_count($string, '>')) { // Text contains unclosed angle brackets }
Но этот метод не обнаружит ошибку:
This is >b<BOLD>/b< word
Я бы не рекомендовал использовать регулярные выражения для такой задачи.
Простая функция для проверки строки для правильно написанных скобок написана быстро:
/** * @param $str input string * @returns boolean true if all brackets are properly opened and closed, false otherwise */ function checkBraces($str) { $strlen = strlen($str); // cache string length for performance $openbraces = 0; for ($i = 0; $i < $strlen; $i++) { $c = $str[$i]; if ($c == '<') // count opening bracket $openbraces++; if ($c == '>') // count closing bracket $openbraces--; if ($openbraces < 0) // check for unopened closing brackets return false; } return $openbraces == 0; // check for unclosed open brackets }
Используя этот код в качестве основы, не должно быть слишком сложно реализовать проверку, чтобы проверить, соответствует ли имя тега открывающих и закрывающих скобок, но я оставлю это вам 🙂
Но этот метод не обнаружит ошибку:
Потому что подсчет имеет смысл только в том случае, если вы хотите проверить, существует ли равное количество открывающих и закрывающих скобок. Но если вы хотите быть добрым к своему пользователю и указать на место, которое он допустил, то подсчета будет недостаточно, и вы должны использовать, т. array_push()
Стек (даже для массива на основе array_push()
и array_pop()
будет достаточно) , С помощью стека вы перебираете свою строку и нажимаете токен, когда вы сталкиваетесь с открытием скобки <
и выталкиваете токен, когда вы нажимаете на закрытие >
. В твоем случае:
This is >b<BOLD>/b< word
вам нужно будет делать pop
поскольку сначала это >
но в стеке ничего нет, так что это вызывает ошибку. Давайте исправим эту скобку и продолжим:
This is <b<BOLD>/b< word
и запустить
push -> ok push -> well if you allow nested brackets, then all is ok, otherwise stack must be empty prior pushing so this bracket is misplaced and you shall throw an error
и т. д. … и как только вы достигнете конца строки, а ваш стек не пуст, вы знаете, что последняя пятнистая пара <
misses its >
(если вы разрешаете вложение в скобки, тогда логика, необходимая для указания того, какая из них потенциально не закрыта, может быть более сложные и иногда дают ложные результаты (например, иногда компиляторы делают в подобном случае)).
Если вы не планируете разрешать вложенные скобки, то вы можете сделать свой код еще проще, поскольку использовать переменную plain integer
чтобы указать, что состояние будет достаточным (например, «0» для <
, 1
для >
и -1
для начального состояния)
Прокручивайте строку по одному символу за раз, если символ является «<» приращением счетчика, и если он «>» уменьшает счетчик. Если счетчик когда-либо становится отрицательным или счетчик не равен нулю, когда вы проходите через строку, то у вас есть закрытые скобки.
Существует регулярное выражение PCRE для проверки правильного количества сбалансированных угловых скобок:
'~\A[^<>]*+(<(?>[^<>]|(?1))*+>[^<>]*+)++\z~'
См. Демо-версию regex
Подробнее см. На странице Согласование сбалансированных конструкций на странице regular-expressions.info.
Вкратце:
\A
– начало строки [^<>]*+
– ноль или более символов, кроме <
и >
(<(?>[^<>]|(?1))*+>[^<>]*+)++
– 1 или более вхождений
<
– открытие <
скобка (?>[^<>]|(?1))*+
– 0 или более вхождений любого символа, отличного от <
и >
(см. [^<>]
), Или всего подпаптерна Group 1 (вызов подпрограммы (?1)
) >
– закрытие >
скобка [^<>]*+
– ноль или более символов, кроме <
и >
\z
– конец строки. Вот регулярное выражение, которое не допускает вложенные скобки.
/^([^<>]*<[^>]*?>[^<>]*)*$/m