在php中搜索不区分大小写的xpath

时间:2010-07-13 16:01:03

标签: php xpath

我有一个像这样的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";

但是,使用此技术,用户必须搜索“芝加哥”而不是“芝加哥”,否则搜索将不返回任何内容。

我知道我需要使用翻译功能,但我的所有试验和错误都是徒劳的。

我试过了:

$upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$lower = "abcdefghijklmnopqrstuvwxyz";
$kw = $xml->xpath("line[contains(text(),'translate('$keyword','$upper','$lower'))]");

但似乎没有任何效果。有小费吗?

3 个答案:

答案 0 :(得分:8)

如果您选择使用,那么Gordon建议在XPath中使用PHP函数将更加灵活。但是,与他的回答相反,XPath 1.0中的translate字符串函数,这意味着您可以使用它;你的问题是如何

首先,查尔斯在对这个问题的评论中指出了明显的错字。然后就是你如何尝试匹配文本值的逻辑。


在单词形式中,您目前正在询问,“该文本是否包含关键字的小写形式?”这不是您想要的。相反,请问,“小写文本是否包含小写关键字?”翻译(原谅双关语)回到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;
}

dijon's comment

之后

修改

在foreach中,您可以访问下面的行号,章节号和书名。

行号 - 这只是<line>元素的一个属性,使得访问它非常容易。使用SimpleXML有两种方法可以访问它:$node['number']$node->attributes()->number(我更喜欢前者)。

章节编号 - 为此,正如您所说,我们需要遍历树。如果我们使用DOM类,我们会有一个方便的$node->parentNode属性直接引导我们到<chapter>(因为它是我们<line>的直接祖先)。 SimpleXML没有这么方便的属性,但是我们可以使用相对的XPath查询来获取它。 parent axis允许我们遍历树。

由于xpath()返回一个数组,我们可以作弊并使用current()来访问从它返回的数组中的第一个(也是唯一的)项。然后,只需访问上面的number属性即可。

// In the near future we can use: current(...)['number'] but not yet
$chapter = current($node->xpath('./parent::chapter'))->attributes()->number;

图书名称 - 此过程与访问章节编号的过程相同。来自<line>的相对XPath查询可以使用./ancestor::book ./parent:chapter/parent::book(或name)。希望您能弄清楚如何访问其{{1}}属性。

答案 1 :(得分:2)

请参阅salathe关于如何使用SimpleXml和translate()的答案。

作为使用XPath函数的替代/添加选项,您可以在使用DOM时在XPath表达式中使用PHP5.3中的任何PHP函数,包括自定义。我不确定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 blog entrythe PHP Manual.

答案 2 :(得分:0)

我可能错过了一些东西......但这是另一种恕我直言的做法 - 更简单。 在通过strtolower()将XML加载到SimpleXML之前使用PHP 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 :: lowercase