У меня есть xml-файл:
<volume name="Early"> <book name="School Years"> <chapter number="1"> <line number="1">Here's the first line with Chicago in it.</line> <line number="2">Here's a line that talks about Atlanta</line> <line number="3">Here's a line that says chicagogo </line> </chapter> </book> </volume>
Я пытаюсь выполнить простой поиск по ключевым словам с помощью PHP, который находит слово и отображает строку, в которой он находился. У меня есть эта работа
$xml = simplexml_load_file($data); $keyword = $_GET['keyword']; $kw=$xml->xpath("//line[contains(text(),'$keyword')]"); ...snip... echo $kw[0]." is the first returned item";
Однако, используя эту технику, пользователь должен искать «Чикаго», а не «chicago», или поиск ничего не вернет.
Я понимаю, что мне нужно использовать функцию перевода, но все мои проб и ошибок были напрасны.
Я пробовал:
$upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; $lower = "abcdefghijklmnopqrstuvwxyz"; $kw = $xml->xpath("line[contains(text(),'translate('$keyword','$upper','$lower'))]");
но ничего не работает. какие-нибудь советы?
Рекомендация Гордона использовать функцию PHP из XPath окажется более гибкой, если вы решите ее использовать. Однако, вопреки его ответу, функция строки translate
доступна в XPath 1.0, поэтому вы можете ее использовать; ваша проблема в том, как .
Во-первых, есть очевидная оговорка, которую Чарльз указал в своем комментарии к этому вопросу. Тогда есть логика того, как вы пытаетесь сопоставить текстовые значения.
В текстовой форме вы в настоящее время спрашиваете: « содержит ли текст нижний регистр ключевого слова? » Это не то, что вы хотите спросить. Вместо этого спросите: « содержит ли нижний регистр ключевое слово в нижнем регистре? » Перевод (pardon the pun), который возвращается в XPath-land:
(Примечание: усеченные алфавиты для удобочитаемости)
//line[contains(translate(text(),'ABC...Z','abc...z'),'chicago')]
Вышеприведенный текст уменьшает текст, содержащийся в узле line
проверяет, содержит ли он (нижний текст) ключевое слово chicago
.
И теперь для обязательного фрагмента кода (но на самом деле, вышеупомянутая идея – это то, что вам действительно нужно забрать домой):
$xml = simplexml_load_file($data); $search = strtolower($keyword); $nodes = $xml->xpath("//line[contains(translate(text(), 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghjiklmnopqrstuvwxyz'), '$search')]"); echo 'Got ' . count($nodes) . ' matches!' . PHP_EOL; foreach ($nodes as $node){ echo $node . PHP_EOL; }
Редактировать после комментария дижона
Внутри foreach вы можете получить доступ к номеру строки, номеру главы и названию книги, как показано ниже.
Номер строки – это только атрибут элемента <line>
который делает доступ к нему супер-простым. Существует два способа доступа к нему через SimpleXML: $node['number']
или $node->attributes()->number
(я предпочитаю первый).
Номер главы – чтобы понять это, как вы правильно сказали, нам нужно пройти по дереву. Если бы мы использовали классы DOM, у нас было бы удобное свойство $node->parentNode
привело бы нас непосредственно к <chapter>
(так как это непосредственный предок нашей <line>
). У SimpleXML нет такого удобного свойства, но мы можем использовать относительный запрос XPath для его получения. Родительская ось позволяет нам перемещаться по дереву.
Поскольку xpath()
возвращает массив, мы можем обманывать и использовать current()
для доступа к первому (и единственному) элементу в возвращаемом из него массиве. Тогда это просто вопрос доступа к атрибуту number
как указано выше.
// In the near future we can use: current(...)['number'] but not yet $chapter = current($node->xpath('./parent::chapter'))->attributes()->number;
Название книги – процесс для этого аналогичен процессу доступа к номеру главы. Относительный запрос XPath из <line>
может использовать ось предка, например ./ancestor::book
(или ./parent:chapter/parent::book
). Надеюсь, вы сможете выяснить, как получить доступ к атрибуту name
.
См. Ответ Salathe о том, как это сделать с помощью SimpleXml и translate ().
В качестве альтернативы / добавленной опции для использования функций XPath вы можете использовать любую PHP-функцию с PHP5.3, включая самоопределяемую, в выражениях XPath при использовании DOM . Я не уверен, что он доступен в SimpleXml.
// create a DOMDocument and load your XML string into it $dom = new DOMDocument; $dom->loadXML($xml); // create a new Xpath and register PHP functions as XPath functions $xPath = new DOMXPath($dom); $xPath->registerNamespace("php", "http://php.net/xpath"); $xPath->registerPHPFunctions(); // Setup the query $keyword = 'chicago'; $q = "//line[php:functionString('stripos', text(), '$keyword')]"; $nodes = $xPath->query($q); // Iterate the resulting NodeList foreach($nodes as $node) { echo $node->nodeValue, PHP_EOL; }
Это приведет к выводу
Here's the first line with Chicago in it. Here's a line that says chicagogo
Для получения дополнительной информации см. Запись в блоге @salathes и Руководство по PHP.
Возможно, я что-то пропустил … но вот еще один подход, который ИМХО – проще. Как насчет использования PHP strtolower()
перед загрузкой XML в SimpleXML через simplexml_load_string()
?
IE
$xml = simplexml_load_string(strtolower(file_get_contents($xml_file_path))); $keyword = strtolower($_GET['keyword']); //Make sure you sanitize this! $kw = $xml->xpath("//line[contains(text(),'$keyword')]");
Таким образом, вы сравниваете строчные буквы: lowercase