XPath ที่ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่มี () เป็นไปได้หรือไม่?


96

ฉันใช้งานกับโหนดข้อความทั้งหมดของ DOM ของฉันและตรวจสอบว่า nodeValue มีสตริงที่แน่นอนหรือไม่

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

นี่เป็นกรณีที่ละเอียดอ่อน แต่ผมยังต้องการที่จะจับTest, หรือTEST TesTเป็นไปได้กับ XPath (ใน JavaScript) หรือไม่?

คำตอบ:


113

สำหรับ 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)
Aron Woost

@AronWoost: อาจจะมีกำไรบ้างเพียงแค่เปรียบเทียบได้หากคุณกระตือรือร้นที่จะค้นหา translate()ตัวเองไม่ได้สนใจว่าคุณมักจะทำซ้ำตัวละครแต่ละตัว - เป็นอย่างเทียบเท่ากับtranslate(., 'EE', 'ee') PS: อย่าลืมโหวต @KirillPolishchuk ความคิดนี้เป็นของเขา translate(., 'E', 'e')
Tomalak

2
System.Xml.XmlNodeList x = mydoc.SelectNodes ("// * [ประกอบด้วย (แปล (text (), 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÉÈÊÀÁÁÂÒÓÔÙÚÛÇÅÏÕÑŒ', 'abcdefghijklmnopqrstuvwxyzäöüéêèêàâòóùô)
Stefan Steiger

1
ฉบับที่ดู"แน่นอนคุณต้องการเพียงการแปลตัวอักษรเหล่านั้นคุณจริงค้นหาคำว่า"ส่วนหนึ่ง
Tomalak

63

สวยงามมากขึ้น:

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

4
+1 อย่างแน่นอน นั่นเป็นสิ่งที่ฉันคิดไม่ถึง (ฉันจะใช้ในคำตอบของฉันนี่ดีกว่ารูทีน JavaScript ดั้งเดิมที่ฉันเขียนไว้มาก)
Tomalak

4
มันจะไม่แปลงTESTเป็นtestและปล่อยให้Testมันเป็นอย่างนั้นเหรอ?
Muhammad Adeel Zahid

8
@MuhammadAdeelZahid - ไม่ใช่มันแทนที่ "T" ด้วย "t", "E" ด้วย "e" ฯลฯ เป็นการจับคู่แบบ 1 ต่อ 1
Daniel Haley

translate(., 'TES', 'tes')มันอาจจะมีความชัดเจนมากขึ้นในการทำ ด้วยวิธีนี้ผู้คนจะรู้ว่ามันไม่ใช่การแปลคำว่าเป็นการแปลตัวอักษร
mlissner

หรือ 'EST,' est 'แม้ว่ามันจะดูดี (แม้ว่าจะคลุมเครือเล็กน้อย) ที่ส่วนหนึ่งของข้อความค้นหาปรากฏในการแมป (ตัวอักษรที่ซ้ำกันถูกลบออก)
George Birbilis

57

โซลูชั่น XPath 2.0

  1. ใช้ตัวพิมพ์เล็ก () :

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

  2. ใช้การจับคู่ regex () การจับคู่ regex กับแฟล็กที่ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่:

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


1
ไวยากรณ์นี้ไม่รองรับใน Firefox และ Chrome หรือไม่ ฉันเพิ่งลองใช้ในคอนโซลและทั้งคู่ส่งคืนข้อผิดพลาดทางไวยากรณ์
db

1
Firefox และ Chrome ใช้เฉพาะ XPath 1.0
kjhughes

ที่ฉันสามารถตรวจสอบได้ว่าจะทำงานตามที่คาดไว้หรือไม่
Ankit Gupta

@AnkitGupta: เครื่องมือออนไลน์หรือออฟไลน์ใด ๆ ที่รองรับ XPath 2.0 สามารถใช้เพื่อตรวจสอบคำตอบนี้ได้แน่นอน แต่คำแนะนำเครื่องมือ (1) ไม่อยู่ในหัวข้อที่นี่ใน SO และ (2) ให้คะแนนโหวต 56 โหวต, 0 ดาวน์โหวตและไม่ ความคิดเห็นที่ไม่เห็นด้วยในช่วงหกปีที่ผ่านมาคุณค่อนข้างมั่นใจว่าคำตอบนี้ถูกต้อง ;-)
kjhughes

8

ใช่. คุณสามารถใช้translateเพื่อแปลงข้อความที่คุณต้องการจับคู่เป็นตัวพิมพ์เล็กได้ดังนี้:

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

6

หากคุณใช้ XPath 2.0 คุณสามารถระบุการเปรียบเทียบเป็นอาร์กิวเมนต์ที่สามที่จะมี () อย่างไรก็ตาม URI การจัดเรียงไม่ได้เป็นมาตรฐานดังนั้นรายละเอียดจึงขึ้นอยู่กับผลิตภัณฑ์ที่คุณใช้

โปรดทราบว่าวิธีแก้ปัญหาที่ให้ไว้ก่อนหน้านี้โดยใช้ translate () ทั้งหมดจะถือว่าคุณใช้ตัวอักษรภาษาอังกฤษ 26 ตัวอักษรเท่านั้น

UPDATE: XPath 3.1 กำหนด URI การเปรียบเทียบมาตรฐานสำหรับการจับคู่ case-blind


4

วิธีที่ฉันทำคือใช้ฟังก์ชัน "translate" ใน XPath ฉันจะไม่บอกว่ามันสวยมาก แต่ทำงานได้อย่างถูกต้อง

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

หวังว่านี่จะช่วยได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.