¿XPath que no distingue entre mayúsculas y minúsculas contiene () posible?

94

Estoy ejecutando todos los nodos de texto de mi DOM y verifico si nodeValue contiene una determinada cadena.

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

Esto distingue entre mayúsculas y minúsculas. Sin embargo, también quiero atrapar Test, TESTo TesT. ¿Es eso posible con XPath (en JavaScript)?

Aron Woost
fuente

Respuestas:

111

Esto es para XPath 1.0. Si su entorno es compatible con XPath 2.0, consulte aquí .


Si. Posible, pero no hermoso.

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

Esto funcionaría para cadenas de búsqueda donde el alfabeto se conoce de antemano. Agregue los caracteres acentuados que espera ver.


Si puede, marque el texto que le interesa con algún otro medio, como encerrarlo en un texto <span>que tenga una determinada clase mientras construye el HTML. Estas cosas son mucho más fáciles de localizar con XPath que las subcadenas en el texto del elemento.

Si esa no es una opción, puede dejar que JavaScript (o cualquier otro lenguaje host que esté utilizando para ejecutar XPath) lo ayude a crear una expresión XPath dinámica:

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')]"

(Sugerencia para la respuesta de @ KirillPolishchuk : por supuesto, solo necesita traducir los caracteres que está buscando ).

Este enfoque funcionaría para cualquier cadena de búsqueda, sin requerir conocimiento previo del alfabeto, lo cual es una gran ventaja.

Ambos métodos anteriores fallan cuando las cadenas de búsqueda pueden contener comillas simples, en cuyo caso las cosas se complican más .

Tomalak
fuente
¡Gracias! Además, la adición es buena, ya que traduce solo los caracteres necesarios. Tendría curiosidad por saber cuál es la victoria por rendimiento. Tenga en cuenta que xpathPrepare () podría manejar los caracteres que aparecen más de una vez de manera diferente (por ejemplo, obtiene TEEEEEST y teeeeest).
Aron Woost
@AronWoost: Bueno, podría haber alguna ganancia, solo compárelo si está ansioso por averiguarlo. translate()a sí mismo no le importa con qué frecuencia repita cada carácter - translate(., 'EE', 'ee')es absolutamente equivalente a translate(., 'E', 'e'). PD: No olvides votar a favor de @KirillPolishchuk, la idea fue suya.
Tomalak
2
System.Xml.
Stefan Steiger
1
No. Vea la parte "por supuesto que solo necesita traducir los caracteres que está buscando" .
Tomalak
61

Más hermosa:

/html/body//text()[contains(translate(., 'TES', 'tes'), 'test')]
Kirill Polishchuk
fuente
4
+1 Absolutamente. Eso es algo en lo que no pensé. (Lo usaré en mi respuesta, esto es mucho mejor que la rutina de JavaScript original que escribí)
Tomalak
4
¿no acaba de convertir TESTa testy la licencia Testcomo es?
Muhammad Adeel Zahid
6
@MuhammadAdeelZahid - No, está reemplazando "T" con "t", "E" con "e", etc. Es una coincidencia 1 a 1.
Daniel Haley
Podría ser más claro hacerlo translate(., 'TES', 'tes'). De esa manera, la gente se dará cuenta de que no es una traducción de palabras, sino una traducción de letras.
mlissner
55

Soluciones XPath 2.0

  1. Utilice minúsculas () :

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

  2. Utilice coincidencias () regex que coinciden con su bandera que no distingue entre mayúsculas y minúsculas:

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

kjhughes
fuente
1
¿Esta sintaxis no es compatible con Firefox y Chrome? Lo intenté en la consola y ambos devuelven un error de sintaxis.
db
1
Firefox y Chrome solo implementan XPath 1.0.
kjhughes
8

Si. Puede usar translatepara convertir el texto que desea hacer coincidir a minúsculas de la siguiente manera:

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

Si está utilizando XPath 2.0, puede especificar una intercalación como tercer argumento para contains (). Sin embargo, los URI de intercalación no están estandarizados, por lo que los detalles dependen del producto que esté utilizando.

Tenga en cuenta que las soluciones dadas anteriormente usando translate () asumen que solo está usando el alfabeto inglés de 26 letras.

ACTUALIZACIÓN: XPath 3.1 define un URI de intercalación estándar para la coincidencia ciega entre mayúsculas y minúsculas.

Michael Kay
fuente
3

La forma en que siempre hice esto fue usando la función "traducir" en XPath. No diré que es muy bonito pero funciona correctamente.

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

espero que esto ayude,

Marvin Smit
fuente