XPath的contains()函数是否支持大小写不敏感?

124
我正在遍历DOM中的所有文本节点,并检查nodeValue是否包含特定的字符串。
/html/body//text()[contains(.,'test')]

这是区分大小写的。但是,我还想捕捉TestTEST或者TesT。在JavaScript中,使用XPath是否有可能实现?

6个回答

139

这是针对XPath 1.0的。如果您的环境支持XPath 2.0,请参见此处


是的,可以实现,但不够优雅。

/html/body//text()[
  contains(
    translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'),
    'test'
  )
]

这将适用于预先知道字母表的搜索字符串。添加您希望看到的任何重音字符。
如果可以的话,在构建HTML时,使用其他方法标记您感兴趣的文本,例如将其包含在具有特定类的<span>中。这些内容比元素文本中的子字符串更容易使用XPath定位。
如果这不是选项,则可以让JavaScript(或任何其他用于执行XPath的宿主语言)帮助您构建动态XPath表达式:
function xpathPrepare(xpath, searchString) {
  return xpath.replace("$u", searchString.toUpperCase())
              .replace("$l", searchString.toLowerCase())
              .replace("$s", searchString.toLowerCase());
}

xp = xpathPrepare("//text()[contains(translate(., '$u', '$l'), '$s')]", "Test");
// -> "//text()[contains(translate(., 'TEST', 'test'), 'test')]"

(感谢@KirillPolishchuk的回答 - 当然,你只需要翻译你实际搜索的字符。)

这种方法适用于任何搜索字符串,而无需事先了解字母表,这是一个很大的优势。

当搜索字符串可以包含单引号时,上述两种方法都会失败,在这种情况下,事情变得更加复杂


另外,添加功能很好,只翻译所需的字符。我很想知道性能提升是多少。请注意,xpathPrepare()可以以不同方式处理出现多次的字符(例如,您会得到TEEEEEST和teeeeest)。 - Aron Woost
@AronWoost:如果你渴望找到答案,那么可能会有一些收获,只需进行基准测试即可。translate()本身并不关心您重复每个字符的次数- translate(., 'EE', 'ee')translate(., 'E', 'e')完全等效。附言:别忘了给@KirillPolishchuk点赞,这个想法是他提出的。 - Tomalak
3
这句代码的含义是:使用mydoc文档对象来选择所有包含单词"foo"(不区分大小写和特定语言字符)的XML节点,并将它们存储在名为x的XmlNodeList对象中。 - Stefan Steiger
1
不需要。请看“当然,你只需要翻译你实际搜索的那些字符”部分。 - Tomalak

74

现代XPath 2.0(及更高版本)解决方案

  1. 使用lower-case()函数:

    /html/body//text()[contains(lower-case(.),'test')]

  2. 使用matches()函数进行正则表达式匹配,并设置不区分大小写的标志:

    /html/body//text()[matches(.,'test', 'i')]

对于旧版仅支持XPath 1.0的环境,请参考@Tomalak's answer中描述的translate()技术。


1
这个语法在Firefox和Chrome中不支持吗?我刚在控制台中尝试了一下,它们都返回语法错误。 - d-b
8
Firefox和Chrome只实现XPath 1.0版本。 - kjhughes
我在哪里可以验证这将按预期工作? - Ankit Gupta
@AnkitGupta:任何支持XPath 2.0的在线或离线工具都可以用来验证这个答案,当然,(1) 工具推荐在SO上是不被允许的,(2) 考虑到这个答案在六年多的时间里获得了56个赞,0个踩,并且没有任何异议的评论,你可以非常有信心地认为这个答案是正确的。;-) - kjhughes

69

不区分大小写的contains

/html/body//text()[contains(translate(., 'EST', 'est'), 'test')]

4
+1 绝对没错。这是我没有想到的。*(我会在我的答案中使用这个,这比我原来编写的JavaScript程序要好得多)* - Tomalak
4
它会将TEST转换为test,而让Test保持不变吗? - Muhammad Adeel Zahid
8
不,它是将大写字母"T"替换为小写字母"t",将大写字母"E"替换为小写字母"e"等等。这是一种一对一的匹配。 - Daniel Haley
2
可能更清晰的做法是使用 translate(., 'TES', 'tes')。这样人们就会意识到这不是单词翻译,而是字母翻译。 - mlissner
1
尽管这部分搜索术语的映射中出现了被删除的重复字母,使其看起来很酷(尽管有点神秘),但它实际上只是“EST”或“est”。 - George Birbilis
1
我们需要 icontains() :-) - But those new buttons though..

11

是的,您可以使用 translate 将要匹配的文本转换为小写,方法如下:

/html/body//text()[contains(translate(., 
                                      'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
                                      'abcdefghijklmnopqrstuvwxyz'),
                   'test')]

7
如果您使用的是XPath 2.0,则可以将排序规则指定为contains()函数的第三个参数。但是,排序规则URI没有标准化,因此具体细节取决于您使用的产品。
请注意,之前使用translate()函数给出的解决方案都假定您只使用26个英文字母。
更新:XPath 3.1定义了一个用于不区分大小写匹配的标准排序规则URI。

7

我通常使用XPath中的“translate”函数来完成这个操作。虽然不太美观,但能正确地工作。

/html/body//text()[contains(translate(.,'abcdefghijklmnopqrstuvwxyz',
                                        'ABCDEFGHIJKLMNOPQRSTUVWXYZ'),'TEST')]

希望这可以帮到您,

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接