XPath insensible à la casse contient () possible?

94

J'exécute tous les textnodes de mon DOM et vérifie si le nodeValue contient une certaine chaîne.

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

Ceci est sensible à la casse. Cependant, je veux aussi attraper Test, TESTou TesT. Est-ce possible avec XPath (en JavaScript)?

Aron Woost
la source

Réponses:

111

Ceci est pour XPath 1.0. Si votre environnement prend en charge XPath 2.0, cliquez ici .


Oui. Possible, mais pas beau.

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

Cela fonctionnerait pour les chaînes de recherche où l'alphabet est connu à l'avance. Ajoutez tous les caractères accentués que vous vous attendez à voir.


Si vous le pouvez, marquez le texte qui vous intéresse avec d'autres moyens, comme le placer dans un <span>qui a une certaine classe lors de la construction du HTML. De telles choses sont beaucoup plus faciles à localiser avec XPath que des sous-chaînes dans le texte de l'élément.

Si ce n'est pas une option, vous pouvez laisser JavaScript (ou tout autre langage hôte que vous utilisez pour exécuter XPath) vous aider à créer une expression XPath dynamique:

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

( Astuce du chapeau à la réponse de @ KirillPolishchuk - bien sûr, il vous suffit de traduire les caractères que vous recherchez réellement .)

Cette approche fonctionnerait pour n'importe quelle chaîne de recherche, sans nécessiter de connaissance préalable de l'alphabet, ce qui est un gros plus.

Les deux méthodes ci-dessus échouent lorsque les chaînes de recherche peuvent contenir des guillemets simples, auquel cas les choses se compliquent .

Tomalak
la source
Merci! L'ajout est également agréable, ne traduisant que les caractères nécessaires. Je serais curieux de savoir quelle est la victoire de la performance. Notez que xpathPrepare () peut gérer différemment les caractères qui apparaissent plusieurs fois (par exemple, vous obtenez TEEEEEST et teeeeest).
Aron Woost
@AronWoost: Eh bien, il pourrait y avoir un gain, comparez-le simplement si vous êtes impatient de le savoir. translate()lui-même ne se soucie pas de la fréquence à laquelle vous répétez chaque caractère - translate(., 'EE', 'ee')est absolument équivalent à translate(., 'E', 'e'). PS: N'oubliez pas de voter @KirillPolishchuk, l'idée était la sienne.
Tomalak
2
System.Xml.XmlNodeList x = mydoc.SelectNodes ("// * [contient (translate (text (), 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÉÈÊÀÁÁÂÒÓÔÙÚÛÇÅÏÕÑŒ', 'abcôùàghijklmnopqrœuvówxyzä')
Stefan Steiger
1
Non. Voir la partie "bien sûr, vous n'avez besoin de traduire que les caractères que vous recherchez" .
Tomalak
61

Plus beau:

/html/body//text()[contains(translate(., 'TES', 'tes'), 'test')]
Kirill Polishchuk
la source
4
+1 Absolument. C'est quelque chose auquel je n'ai pas pensé. (J'utiliserai cela dans ma réponse, c'est bien mieux que la routine JavaScript originale que j'ai écrite)
Tomalak
4
ne serait - il convertir juste TESTà testet congé Testcomme il est?
Muhammad Adeel Zahid
6
@MuhammadAdeelZahid - Non, il remplace "T" par "t", "E" par "e", etc. C'est un match de 1 contre 1.
Daniel Haley
Cela pourrait être plus clair à faire translate(., 'TES', 'tes'). De cette façon, les gens comprendront que ce n'est pas une traduction de mots, mais une traduction de lettres.
mlissner
55

Solutions XPath 2.0

  1. Utilisez des minuscules () :

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

  2. Utilisez matches () regex correspondant avec son indicateur insensible à la casse:

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

kjhughes
la source
1
Cette syntaxe n'est-elle pas prise en charge dans Firefox et Chrome? Je viens de l'essayer dans la console et ils renvoient tous les deux une erreur de syntaxe.
db
1
Firefox et Chrome n'implémentent que XPath 1.0.
kjhughes
8

Oui. Vous pouvez utiliser translatepour convertir le texte que vous souhaitez faire correspondre en minuscules comme suit:

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

Si vous utilisez XPath 2.0, vous pouvez spécifier un classement comme troisième argument de contains (). Cependant, les URI de classement ne sont pas standardisés, de sorte que les détails dépendent du produit que vous utilisez.

Notez que les solutions données précédemment avec translate () supposent toutes que vous n'utilisez que l'alphabet anglais de 26 lettres.

MISE À JOUR: XPath 3.1 définit un URI de classement standard pour la correspondance sans casse.

Michael Kay
la source
3

La façon dont j'ai toujours fait cela a été d'utiliser la fonction "traduire" dans XPath. Je ne dirai pas que c'est très joli mais cela fonctionne correctement.

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

J'espère que cela t'aides,

Marvin Smit
la source