Возможно ли XPath без учета регистра ()?

94

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

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

Это чувствительно к регистру. Однако я тоже хочу поймать Test, TESTили TesT. Возможно ли это с XPath (в JavaScript)?

Арон Вуст
источник

Ответы:

112

Это для 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).
Арон Вуст,
@AronWoost: Что ж, может быть некоторый выигрыш, просто сравните его, если хотите узнать. translate()Сама по себе не волнует, как часто вы повторяете каждый символ - translate(., 'EE', 'ee')абсолютно эквивалентно translate(., 'E', 'e'). PS: Не забудьте проголосовать за @KirillPolishchuk, идея была его.
Tomalak
2
System.Xml.XmlNodeList x = mydoc.SelectNodes ("// * [содержит (translate (text (), 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÉÀÁÒÓÔÙÚÛÇÅÏÕÑŒ', 'abcdefghijklmnopqrstuvwxyzäçâóè);" f
Stefan Steiger
1
Нет. См. Раздел «Конечно, вам нужно перевести только те символы, которые вы действительно ищете» .
Tomalak
62

Более красивый:

/html/body//text()[contains(translate(., 'TES', 'tes'), 'test')]
Кирилл Полищук
источник
4
+1 Совершенно верно. Я не думал об этом. (Я использую это в своем ответе, это намного лучше, чем исходная процедура JavaScript, которую я написал)
Tomalak
4
не было бы просто преобразовать TESTв testи оставить Testкак есть?
Мухаммад Адил Захид
7
@MuhammadAdeelZahid - Нет, он заменяет "T" на "t", "E" на "e" и т. Д. Это совпадение один к одному.
Дэниел Хейли
Может быть, будет более понятным translate(., 'TES', 'tes'). Так люди поймут, что это не перевод слова, а буквальный.
mlissner 01
или 'EST,' est ', хотя это выглядит круто (хотя и немного загадочно), что часть искомого термина появляется в сопоставлении (повторяющиеся буквы удалены)
Джордж Бирбилис
56

Решения XPath 2.0

  1. Используйте строчные буквы () :

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

  2. Используйте сопоставление регулярного выражения match () с его флагом без учета регистра:

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

Kjhughes
источник
1
Этот синтаксис не поддерживается в Firefox и Chrome? Я просто попробовал это в консоли, и оба они возвращают синтаксическую ошибку.
db
1
Firefox и Chrome реализуют только XPath 1.0.
kjhughes 07
где я могу проверить, что это будет работать должным образом?
Анкит Гупта,
@AnkitGupta: Конечно, для проверки этого ответа можно использовать любой онлайн- или офлайн-инструмент, поддерживающий XPath 2.0, но (1) рекомендации по инструментам здесь не по теме, и (2) с учетом 56 голосов за, 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

Я всегда делал это с помощью функции "translate" в XPath. Не скажу, очень красиво, но работает корректно.

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

надеюсь это поможет,

Марвин Смит
источник