XPath case insensitive contiene () possibile?


94

Sto correndo su tutti i textnodes del mio DOM e controllo se il nodeValue contiene una determinata stringa.

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

Questo fa distinzione tra maiuscole e minuscole. Tuttavia, voglio anche catturare Test, TESTo TesT. È possibile con XPath (in JavaScript)?

Risposte:


111

Questo è per XPath 1.0. Se il tuo ambiente supporta XPath 2.0, vedi qui .


Sì. Possibile, ma non bello.

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

Questo funzionerebbe per le stringhe di ricerca in cui l'alfabeto è noto in anticipo. Aggiungi i caratteri accentati che ti aspetti di vedere.


Se puoi, contrassegna il testo che ti interessa con altri mezzi, come racchiuderlo in una <span>che ha una certa classe durante la creazione dell'HTML. Tali cose sono molto più facili da individuare con XPath rispetto alle sottostringhe nel testo dell'elemento.

Se questa non è un'opzione, puoi lasciare che JavaScript (o qualsiasi altro linguaggio host che stai utilizzando per eseguire XPath) ti aiuti a creare un'espressione XPath dinamica:

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

(Punta di cappello alla risposta di @ KirillPolishchuk - ovviamente devi solo tradurre quei caratteri che stai effettivamente cercando .)

Questo approccio funzionerebbe per qualsiasi stringa di ricerca, senza richiedere una conoscenza preliminare dell'alfabeto, il che è un grande vantaggio.

Entrambi i metodi precedenti falliscono quando le stringhe di ricerca possono contenere virgolette singole, nel qual caso le cose si complicano .


Grazie! Anche l'aggiunta è carina, traducendo solo i caratteri necessari. Sarei curioso di sapere quale sia la vittoria in termini di prestazioni. Nota che xpathPrepare () potrebbe gestire caratteri che appaiono più di una volta in modo diverso (ad esempio, ottieni TEEEEEST e teeeeest).
Aron Woost

@AronWoost: Beh, potrebbe esserci qualche guadagno, basta fare un benchmark se non vedi l'ora di scoprirlo. translate()di per sé non importa quanto spesso ripeti ogni personaggio - translate(., 'EE', 'ee')è assolutamente equivalente a translate(., 'E', 'e'). PS: non dimenticare di votare @KirillPolishchuk, l'idea era sua.
Tomalak

2
System.
Stefan Steiger

1
No. Vedi la parte "ovviamente devi solo tradurre quei caratteri che stai effettivamente cercando" .
Tomalak

61

Più bello:

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

4
+1 assolutamente. È qualcosa a cui non ho pensato. (Lo userò nella mia risposta, è molto meglio della routine JavaScript originale che ho scritto)
Tomalak

4
non sarebbe solo convertire TESTper teste congedi Testcosì com'è?
Muhammad Adeel Zahid

6
@ MuhammadAdeelZahid - No, sta sostituendo "T" con "t", "E" con "e", ecc. È una corrispondenza 1 a 1.
Daniel Haley

Potrebbe essere più chiaro da fare translate(., 'TES', 'tes'). In questo modo le persone capiranno che non è una traduzione di parole, ma una traduzione di lettere.
mlissner

55

Soluzioni XPath 2.0

  1. Usa lettere minuscole () :

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

  2. Usa la corrispondenza delle espressioni regolari match () con il suo flag senza distinzione tra maiuscole e minuscole:

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


1
Questa sintassi non è supportata in Firefox e Chrome? L'ho appena provato nella console ed entrambi restituiscono un errore di sintassi.
db

1
Firefox e Chrome implementano solo XPath 1.0.
kjhughes

8

Sì. È possibile utilizzare translateper convertire il testo che si desidera abbinare in minuscolo come segue:

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

6

Se stai usando XPath 2.0, puoi specificare un confronto come terzo argomento di contains (). Tuttavia, gli URI delle regole di confronto non sono standardizzati, quindi i dettagli dipendono dal prodotto che stai utilizzando.

Nota che le soluzioni fornite in precedenza usando translate () presumono tutte che tu stia usando solo l'alfabeto inglese di 26 lettere.

AGGIORNAMENTO: XPath 3.1 definisce un URI di confronto standard per la corrispondenza tra maiuscole e minuscole.


3

Il modo in cui l'ho sempre fatto è stato utilizzando la funzione "translate" in XPath. Non dico che sia molto carino ma funziona correttamente.

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

spero che questo ti aiuti,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.