Нечутливий до регістру XPath містить () можливо?


94

Я запускаю всі текстові вузли мого DOM і перевіряю, чи містить nodeValue певний рядок.

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

Це чутливо до регістру. Однак я теж хочу зловити Test, TESTабо TesT. Чи можливо це за допомогою XPath (у JavaScript)?

Відповіді:


111

Це для XPath 1.0. Якщо ваше середовище підтримує XPath 2.0, дивіться тут .


Так. Можливо, але не красиво.

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

Це буде працювати для пошукових рядків, де алфавіт відомий заздалегідь. Додайте будь-які символи з наголосом, які ви очікуєте бачити.


Якщо можете, позначте текст, який вас цікавить, іншими способами, наприклад, вкладіть його в текст, <span>який має певний клас під час побудови HTML. Такі речі набагато легше знайти за допомогою 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'). PS: Не забудьте проголосувати за @KirillPolishchuk, ідея була його.
Томалак

2
System.Xml.XmlNodeList x = mydoc.SelectNodes ("// * [містить (перекласти (текст (), 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÉÈÊÀÁÂÒÓÔÙÚÛÇÅÏÕÑŒ', 'abcdefghijklmnopqrstuvwxyzäöüéïùñë' ');
Stefan Steiger

1
Ні. Дивіться частину "звичайно, вам потрібно перекласти лише ті символи, яких ви насправді шукаєте" .
Томалак

61

Красивішим:

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

4
+1 Абсолютно. Це те, про що я не думав. (Я використаю це у своїй відповіді, це набагато краще, ніж оригінальна рутина JavaScript, яку я писав)
Томалак

4
чи не буде просто перетворити TESTв testі відпустку , Testяк це?
Мухаммед Адель Захід

6
@MuhammadAdeelZahid - Ні, це замінює "T" на "t", "E" на "e" тощо. Це поєднання 1: 1.
Даніель Хейлі,

Це може бути зрозуміліше зробити translate(., 'TES', 'tes'). Таким чином люди зрозуміють, що це не переклад слів, а переклад букв.
mlissner

55

Рішення XPath 2.0

  1. Використовуйте малі регістри () :

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

  2. Використовуйте відповідність регулярного виразу match () із його прапорцем, який не враховує регістр:

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


1
Цей синтаксис не підтримується у Firefox та Chrome? Я просто спробував це в консолі, і вони обидва повертають синтаксичну помилку.
дб

1
Firefox та Chrome реалізують лише XPath 1.0.
kjhughes

8

Так. Можна використовуватиtranslate для перетворення тексту, якому ви хочете відповідати, у нижчу регістр, як показано нижче:

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

6

Якщо ви використовуєте XPath 2.0, ви можете вказати сортування як третій аргумент для contains (). Однак URI порівняння не стандартизовані, тому деталі залежать від продукту, який ви використовуєте.

Зверніть увагу, що всі рішення, наведені раніше за допомогою translate (), передбачають, що ви використовуєте лише 26-літерний англійський алфавіт.

ОНОВЛЕННЯ: XPath 3.1 визначає стандартний URI зіставлення для збігу регістрів.


3

Я завжди це робив, використовуючи функцію "перекласти" в XPath. Я не скажу, що це дуже красиво, але він працює правильно.

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

сподіваюся, це допоможе,

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.