【问题标题】:Case insensitive XPath contains() possible?不区分大小写的 XPath contains() 可能吗?
【发布时间】:2012-01-18 10:19:26
【问题描述】:

我正在遍历我的 DOM 的所有文本节点并检查 nodeValue 是否包含某个字符串。

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

这是区分大小写的。但是,我也想抓住TestTESTTesT。使用 XPath(在 JavaScript 中)可以做到这一点吗?

【问题讨论】:

    标签: javascript html xml xslt xpath


    【解决方案1】:

    这适用于 XPath 1.0。如果您的环境支持 XPath 2.0,请参阅 here


    是的。可能,但不漂亮。

    /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's answer 的提示 - 当然,您只需要翻译您实际搜索的那些字符。)

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

    当搜索字符串可以包含单引号时,上述两种方法都会失败,在这种情况下会得到more complicated

    【讨论】:

    • 谢谢!添加也很好,只翻译需要的字符。我很好奇性能胜利是什么。请注意,xpathPrepare() 可以以不同的方式处理不止一次出现的字符(例如,您得到 TEEEEEST 和 teeeeest)。
    • @AronWoost:嗯,可能会有一些收获,如果你想知道的话,只需对它进行基准测试。 translate() 本身并不关心您重复每个字符的频率 - translate(., 'EE', 'ee') 绝对等同于 translate(., 'E', 'e')P.S.:别忘了给@KirillPolishchuk 投票,这个想法是他的。
    • system.xml.xmlnodelist x = mydoc.selectnodes(“// * [包含(翻译(texts(text(text(text(text(text))abcdefghijklmnopqrstuvwxyzäönçåïõñhrstuvwxyzäönçåðñœ','abcdefghijklmnopqrsuvwwxyzäöéíèêáááóóóôȱúöçåïõêááááóóôȱúȱçåȱȱȱ's,'foo')]”);
    • 没有。见“当然你只需要翻译那些你实际搜索的字符”部分。
    【解决方案2】:

    不区分大小写contains

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

    【讨论】:

    • +1 绝对。这是我没有想到的。 (我将在我的回答中使用它,这比我编写的原始 JavaScript 例程要好得多)
    • 难道不只是将TEST 转换为test 并保留Test 原样吗?
    • @MuhammadAdeelZahid - 不,它用“t”替换“T”,用“e”替换“E”等。这是一对一的匹配。
    • translate(., 'TES', 'tes') 可能更清楚。这样人们就会意识到这不是单词翻译,而是字母翻译。
    • 或 'EST, 'est',虽然看起来很酷(尽管有点神秘),部分搜索词出现在映射中(重复的字母被删除)
    【解决方案3】:

    XPath 2.0 解决方案

    1. 使用lower-case():

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

    2. 使用matches() 正则表达式匹配其不区分大小写 标志:

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

    【讨论】:

    • Firefox 和 Chrome 不支持这种语法吗?我刚刚在控制台中尝试过,它们都返回语法错误。
    • Firefox 和 Chrome 仅实现 XPath 1.0。
    • 在哪里可以验证这是否会按预期工作?
    • @AnkitGupta:当然,任何支持 XPath 2.0 的在线或离线工具都可以用来验证这个答案,但是(1)工具推荐在 SO 上是题外话,(2)鉴于56 次赞成票,0 次反对票,并且在六年多的时间里没有反对意见,您可以非常确信这个答案是正确的。 ;-)
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

      如果您使用的是 XPath 2.0,那么您可以将排序规则指定为 contains() 的第三个参数。但是,排序规则 URI 不是标准化的,因此详细信息取决于您使用的产品。

      请注意,前面使用 translate() 给出的解决方案都假设您只使用 26 个字母的英文字母表。

      更新: XPath 3.1 为区分大小写匹配定义了标准排序规则 URI。

      【讨论】:

        【解决方案6】:

        我一直这样做的方式是使用 XPath 中的“翻译”功能。我不会说它非常漂亮,但它可以正常工作。

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

        希望这会有所帮助,

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-10-03
          • 2023-03-25
          • 2022-01-23
          • 1970-01-01
          相关资源
          最近更新 更多