ОБНОВЛЕНИЕ: Спасибо всем за ваш вклад. Дополнительная информация.
На самом деле это всего лишь небольшая часть разметки (20 строк), с которой я работаю, и намеревался использовать регулярное выражение для выполнения этой работы.
У меня также есть возможность взломать сценарий (электронный коммерческий), чтобы вставить классы по мере создания навигации. Я хотел ограничить количество хаков, которые у меня есть, чтобы облегчить себе жизнь, когда я перехожу к обновлению до последней версии программного обеспечения.
С учетом сказанного, я прекрасно понимаю мою ситуацию и различные варианты, доступные мне. Первая часть моего регулярного выражения работает так, как ожидалось. Я опубликовал действительно более-менее, чтобы узнать, скажет ли кто-нибудь: «Эй, манекен, это просто просто изменить это …..»
После того, как я приблизился к нескольким моим усилиям, в этот момент это скорее принцип. Чтобы просто знать (и учиться) решение существует для этой проблемы. Я также ненавижу быть избитым куском кода.
ОРИГИНАЛ:
Я пытаюсь использовать регулярные выражения, чтобы добавить класс CSS в первый и последний элементы списка в упорядоченном списке. Я пробовал кучу разных способов, но не могу представить результаты, которые я ищу.
У меня есть регулярное выражение для первого элемента списка, но не может показаться правильным для последнего. Вот с чем я работаю:
$patterns = array('/<ul+([^<]*)<li/m', '/<([^<]*)(?<=<li)(.*)<\/ul>/s'); $replace = array('<ul$1<li class="first"','<li class="last"$2$3</ul>'); $navigation = preg_replace($patterns, $replace, $navigation);
Любая помощь будет принята с благодарностью.
У Джейми Завински есть что сказать об этом …
У вас есть правильный парсер HTML? Я не знаю, есть ли что-то вроде hpricot для PHP, но это правильный способ справиться с этим. Вы могли бы хотя бы нанять hpricot, чтобы сделать первую очистку для вас.
Если вы действительно генерируете HTML – сделайте это там. Похоже, вы хотите создать некоторую навигацию и на ней есть .first
и .last
. Сделайте шаг назад и попробуйте это.
+1 для создания правильного html в качестве наилучшего варианта.
Но совершенно другой подход, который может или не может быть приемлемым для вас: вы можете использовать javascript.
Это использует jquery, чтобы упростить …
$(document).ready( function() { $('#id-of-ul:firstChild').addClass('first'); $('#id-of-ul:lastChild').addClass('last'); } );
Как я уже сказал, может или не может быть использовано в этом случае, но я думаю, что это допустимое решение проблемы в некоторых случаях.
PS: Вы говорите, упорядоченный список, а затем дайте ul в вашем примере. ol = упорядоченный список, ul = неупорядоченный список
Вы написали:
$patterns = array('/<ul+([^<]*)<li/m','/<([^<]*)(?<=<li)(.*)<\/ul>/s');
Первый шаблон:
ul+
=> вы ищете что-то вроде ullll …
Модификатор m здесь бесполезен, так как вы не используете ^ или $.
Второй шаблон:
Использование. * Вместе с s является «опасным», потому что вы можете выбрать весь документ до последней / ul страницы …
И хорошо, я бы просто сбросил s модификатор и использовал: (<li\s)(.*?</li>\s*</ul>)
с заменой: '$1class="last" $2'
Ввиду вышеприведенных замечаний я бы написал первое выражение: <ul.*?>\s*<li
Хотя я устал видеть цитату Джейми Завински каждый раз, когда возникает вопрос о регулярном выражении, Дастин прав, указывая на парсер HTML (или просто генерируя правильный HTML с самого начала!): Регулярные выражения и HTML не смешиваются хорошо, потому что синтаксис HTML является сложным, и, если вы не действуете на хорошо известном машинный сгенерированный вывод с очень предсказуемым результатом, вы склонны что-то ломать в некоторых случаях.
Я не знаю, больше ли кто-то заботится, но у меня есть решение, которое работает в моем простом случае (и я считаю, что это должно работать в общем случае).
Во-первых, позвольте мне указать на две вещи: хотя PhiLho прав в том, что s «опасен», поскольку точки могут соответствовать всем до финала документа, это вполне может быть тем, что вы хотите. Это становится проблемой только с плохо сформированными страницами. Будьте осторожны с любым таким регулярным выражением на больших страницах, написанных вручную.
Во-вторых, php имеет особое значение обратных косых черт, даже в одинарных кавычках. Большинство regexen будут хорошо работать в любом случае, но на всякий случай вы должны всегда избегать их.
Теперь, вот мой код:
<?php $navigation='<ul> <li>Coffee</li> <li>Tea</li> <li>Milk</li> <li>Beer</li> <li>Water</li> </ul>'; $patterns = array('/<ul.*?>\\s*<li/', '/<li((.(?<!<li))*?<\\/ul>)/s'); $replace = array('$0 class="first"', '<li class="last"$1'); $navigation = preg_replace($patterns, $replace, $navigation); echo $navigation; ?>
Это приведет к выводу
<ul> <li class="first">Coffee</li> <li>Tea</li> <li>Milk</li> <li>Beer</li> <li class="last">Water</li> </ul>
Это не предполагает перевода строк внутри открытого тега <ul …>. Если они есть, используйте также модификатор s в первом выражении.
Магия происходит в (.(?<!<li))*?
, Это будет соответствовать любому символу (точка), который не является началом строки <li , повторяется любое количество раз (*) не-жадным способом (?).
Конечно, все это нужно было бы расширить, если есть вероятность, что элементы списка уже имеют атрибут класса . Кроме того, если есть только один элемент списка, он будет совпадать дважды, давая ему два таких атрибута. По крайней мере, для xhtml это нарушит проверку.
Вы можете загрузить навигацию в объекте SimpleXML и работать с этим. Это мешает вам взломать разметку каким-то сумасшедшим регулярным выражением 🙂
Как предисловие .. это waaay чрезмерно усложняющие вещи в большинстве случаев использования. Пожалуйста, см. Другие ответы для более здравого смысла 🙂
Вот небольшой класс PHP, который я написал для решения аналогичной проблемы. Он добавляет «первый», «последний» и любые другие классы, которые вы хотите. Он будет обрабатывать лики без атрибута «class», а также те, которые уже имеют некоторый класс (ы).
<?php /** * Modify list items in pre-rendered html. * * Usage Example: * $replaced_text = ListAlter::addClasses($original_html, array('cool', 'awsome')); */ class ListAlter { private $classes = array(); private $classes_found = FALSE; private $count = 0; private $total = 0; // No public instances. private function __construct() {} /** * Adds 'first', 'last', and any extra classes you want. */ static function addClasses($html, $extra_classes = array()) { $instance = new self(); $instance->classes = $extra_classes; $total = preg_match_all('~<li([^>]*?)>~', $html, $matches); $instance->total = $total ? $total : 0; return preg_replace_callback('~<li([^>]*?)>~', array($instance, 'processListItem'), $html); } private function processListItem($matches) { $this->count++; $this->classes_found = FALSE; $processed = preg_replace_callback('~(\w+)="(.*?)"~', array($this, 'appendClasses'), $matches[0]); if (!$this->classes_found) { $classes = $this->classes; if ($this->count == 1) { $classes[] = 'first'; } if ($this->count == $this->total) { $classes[] = 'last'; } if (!empty($classes)) { $processed = rtrim($matches[0], '>') . ' class="' . implode(' ', $classes) . '">'; } } return $processed; } private function appendClasses($matches) { array_shift($matches); list($name, $value) = $matches; if ($name == 'class') { $value = array_filter(explode(' ', $value)); $value = array_merge($value, $this->classes); if ($this->count == 1) { $value[] = 'first'; } if ($this->count == $this->total) { $value[] = 'last'; } $value = implode(' ', $value); $this->classes_found = TRUE; } return sprintf('%s="%s"', $name, $value); } }