Groß- und Kleinschreibung beachten xpath enthält () möglich?

92

Ich laufe über alle Textknoten meines DOM und überprüfe, ob der nodeValue eine bestimmte Zeichenfolge enthält.

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

Dies unterscheidet zwischen Groß- und Kleinschreibung. Ich möchte aber auch fangen Test, TESToder TesT. Ist das mit XPath (in JavaScript) möglich?

Aron Woost
quelle

Antworten:

110

Dies ist für XPath 1.0. Wenn Ihre Umgebung XPath 2.0 unterstützt, lesen Sie hier .


Ja. Möglich, aber nicht schön.

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

Dies würde für Suchzeichenfolgen funktionieren, bei denen das Alphabet vorher bekannt ist. Fügen Sie alle Zeichen mit Akzent hinzu, die Sie erwarten.


Wenn Sie können, markieren Sie den Text, der Sie interessiert, mit anderen Mitteln, z. B. indem Sie ihn <span>beim Erstellen des HTML- Codes in einen Text einschließen , der eine bestimmte Klasse hat. Solche Dinge sind mit XPath viel einfacher zu finden als Teilzeichenfolgen im Elementtext.

Wenn dies keine Option ist, können Sie JavaScript (oder eine andere Hostsprache, mit der Sie XPath ausführen) beim Erstellen eines dynamischen XPath-Ausdrucks unterstützen:

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

( Hutspitze zu @ KirillPolishchuks Antwort - natürlich müssen Sie nur die Zeichen übersetzen, nach denen Sie tatsächlich suchen .)

Dieser Ansatz würde für jede Suchzeichenfolge funktionieren, ohne dass Vorkenntnisse des Alphabets erforderlich sind, was ein großes Plus ist.

Beide oben genannten Methoden schlagen fehl, wenn Suchzeichenfolgen einfache Anführungszeichen enthalten können. In diesem Fall werden die Dinge komplizierter .

Tomalak
quelle
Vielen Dank! Auch der Zusatz ist nett und übersetzt nur die benötigten Zeichen. Ich wäre gespannt, was der Leistungsgewinn ist. Beachten Sie, dass xpathPrepare () mehr als einmal erscheinende Zeichen unterschiedlich behandeln kann (z. B. erhalten Sie TEEEEEST und teeeeest).
Aron Woost
@AronWoost: Nun, es könnte einen Gewinn geben, messen Sie es einfach, wenn Sie es herausfinden möchten. translate()selbst ist es egal, wie oft Sie jedes Zeichen wiederholen - translate(., 'EE', 'ee')ist absolut gleichbedeutend mit translate(., 'E', 'e'). PS: Vergiss nicht, @KirillPolishchuk zu stimmen, die Idee war seine.
Tomalak
2
System.Xml.XmlNodeList x = mydoc.SelectNodes ("// * [enthält (übersetzen (text ()), '
Stefan Steiger
1
Nein. Siehe den Abschnitt "Natürlich müssen Sie nur die Zeichen übersetzen, nach denen Sie tatsächlich suchen" .
Tomalak
59

Schöner:

/html/body//text()[contains(translate(., 'TES', 'tes'), 'test')]
Kirill Polishchuk
quelle
4
+1 Absolut. Daran habe ich nicht gedacht. (Ich werde das in meiner Antwort verwenden, dies ist viel besser als die ursprüngliche JavaScript-Routine, die ich geschrieben habe)
Tomalak
4
würde es nicht umbauen TESTzu testund lassen , Testwie es ist?
Muhammad Adeel Zahid
6
@MuhammadAdeelZahid - Nein, es ersetzt "T" durch "t", "E" durch "e" usw. Es ist ein 1: 1-Spiel.
Daniel Haley
Es könnte klarer sein, dies zu tun translate(., 'TES', 'tes'). Auf diese Weise werden die Leute erkennen, dass es sich nicht um eine Wortübersetzung handelt, sondern um eine Buchstabenübersetzung.
mlissner
53

XPath 2.0-Lösungen

  1. Verwenden Sie Kleinbuchstaben () :

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

  2. Verwenden Sie match () Regex Matching mit dem Flag, bei dem die Groß- und Kleinschreibung nicht berücksichtigt wird:

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

kjhughes
quelle
1
Wird diese Syntax in Firefox und Chrome nicht unterstützt? Ich habe es gerade in der Konsole versucht und beide geben einen Syntaxfehler zurück.
db
1
Firefox und Chrome implementieren nur XPath 1.0.
kjhughes
8

Ja. Sie können translateden Text, den Sie abgleichen möchten, wie folgt in Kleinbuchstaben konvertieren:

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

Wenn Sie XPath 2.0 verwenden, können Sie eine Kollatierung als drittes Argument für enthalten () angeben. Kollatierungs-URIs sind jedoch nicht standardisiert, sodass die Details von dem von Ihnen verwendeten Produkt abhängen.

Beachten Sie, dass die zuvor mit translate () angegebenen Lösungen davon ausgehen, dass Sie nur das englische Alphabet mit 26 Buchstaben verwenden.

UPDATE: XPath 3.1 definiert einen Standard-Kollatierungs-URI für den fallblinden Abgleich.

Michael Kay
quelle
3

Ich habe das immer mit der Funktion "Übersetzen" in XPath gemacht. Ich werde nicht sagen, dass es sehr hübsch ist, aber es funktioniert richtig.

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

hoffe das hilft,

Marvin Smit
quelle