Regex: InCombiningDiacriticalMarks คืออะไร?


86

รหัสต่อไปนี้เป็นที่รู้จักกันดีในการแปลงอักขระเน้นเสียงเป็นข้อความธรรมดา:

Normalizer.normalize(text, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "");

ฉันแทนที่วิธี "ทำด้วยมือ" ด้วยวิธีนี้ แต่ฉันต้องเข้าใจส่วน "regex" ของการแทนที่ทั้งหมด

1) "InCombiningDiacriticalMarks" คืออะไร?
2) เอกสารอยู่ที่ไหน? (และคนอื่น ๆ ?)

ขอบคุณ.


ดูที่stackoverflow.com/a/29111105/32453เห็นได้ชัดว่ามี "เครื่องหมายรวม" ใน Unicode มากกว่าเครื่องหมายกำกับเสียงเช่นเดียวกับหมายเหตุ
rogerdpack

คำตอบ:


74

\p{InCombiningDiacriticalMarks}เป็นคุณสมบัติบล็อก Unicode ใน JDK7 คุณจะสามารถเขียนได้โดยใช้สัญกรณ์สองส่วน\p{Block=CombiningDiacriticalMarks}ซึ่งอาจชัดเจนกว่าสำหรับผู้อ่าน เป็นเอกสารที่นี่ใน UAX # 44:“การ Unicode ตัวฐานข้อมูล”

ความหมายคือจุดรหัสอยู่ในช่วงหนึ่งซึ่งเป็นบล็อกที่ได้รับการจัดสรรให้ใช้สำหรับสิ่งต่างๆตามชื่อนั้น นี่เป็นแนวทางที่ไม่ดีเนื่องจากไม่มีการรับประกันว่าจุดรหัสในช่วงนั้นเป็นหรือไม่ใช่สิ่งใดสิ่งหนึ่งโดยเฉพาะและรหัสที่ชี้นอกบล็อกนั้นไม่ได้มีอักขระเหมือนกัน

ตัวอย่างเช่นมีตัวอักษรละตินอยู่ใน\p{Latin_1_Supplement}บล็อกเช่นé, U + 00E9 อย่างไรก็ตามมีสิ่งที่ไม่ใช่อักษรละตินอยู่ที่นั่นด้วย และแน่นอนว่ายังมีอักษรละตินอยู่ทั่วทุกแห่ง

บล็อกแทบไม่เคยเป็นสิ่งที่คุณต้องการ

ในกรณีนี้ฉันสงสัยว่าคุณอาจต้องการใช้อสังหาริมทรัพย์\p{Mn}หรือที่เรียก\p{Nonspacing_Mark}ว่า จุดรหัสทั้งหมดในบล็อก Combining_Diacriticals เป็นประเภทนั้น นอกจากนี้ยังมี (ณ Unicode 6.0.0) 1087 Nonspacing_Marks ที่ไม่ได้อยู่ในบล็อกนั้น

เกือบจะเหมือนกับการตรวจสอบ\p{Bidi_Class=Nonspacing_Mark}แต่ไม่มากนักเนื่องจากกลุ่มนั้นมีเครื่องหมายปิดล้อม\p{Me}ด้วย หากคุณต้องการทั้งสองอย่างคุณสามารถพูดได้ว่า[\p{Mn}\p{Me}]คุณกำลังใช้ Java regex engine หรือไม่เนื่องจากมันให้การเข้าถึงคุณสมบัติ General_Category เท่านั้น

คุณต้องใช้ JNI เพื่อไปที่ไลบรารี ICU C ++ regex แบบเดียวกับที่ Google ทำเพื่อเข้าถึงบางอย่างเช่น\p{BC=NSM}ตอนนี้มีเพียง ICU และ Perl เท่านั้นที่ให้การเข้าถึงคุณสมบัติ Unicode ทั้งหมด ไลบรารี regex Java ปกติรองรับคุณสมบัติ Unicode มาตรฐานเพียงสองสามคุณสมบัติ ใน JDK7 แม้ว่าจะมีการรองรับ Unicode Script ที่เหมาะสมซึ่งเป็นที่นิยมอย่างมากสำหรับคุณสมบัติ Block ดังนั้นคุณสามารถใน JDK7 เขียน\p{Script=Latin}หรือ \p{SC=Latin}หรือทางลัด\p{Latin}เพื่อรับอักขระใดก็ได้จากสคริปต์ละติน นำไปสู่การนี้มาก[\p{Latin}\p{Common}\p{Inherited}]ที่จำเป็นโดยทั่วไป

โปรดทราบว่านั่นจะไม่ลบสิ่งที่คุณอาจคิดว่าเป็นเครื่องหมาย "เน้นเสียง" ออกจากตัวอักษรทั้งหมด! มีหลายอย่างที่จะไม่ทำเพื่อ ตัวอย่างเช่นคุณไม่สามารถแปลงĐเป็นDหรือøเป็นoวิธีนั้นได้ เพื่อที่คุณจะต้องลดจุดรหัสเป็นจุดที่ตรงกับความแรงในการเรียงหลักเดียวกันในตารางการเรียงแบบ Unicode

อีกสถานที่หนึ่งที่\p{Mn}สิ่งนี้ล้มเหลวแน่นอนว่ามีการปิดเครื่องหมายเหมือน\p{Me}อย่างเห็นได้ชัด แต่ก็มี\p{Diacritic}อักขระที่ไม่ใช่เครื่องหมาย น่าเศร้าที่คุณต้องการการสนับสนุนด้านทรัพย์สินอย่างเต็มที่ซึ่งหมายความว่า JNI ไปที่ ICU หรือ Perl Java มีปัญหามากมายกับการรองรับ Unicode ฉันกลัว

เดี๋ยวก่อนฉันเห็นว่าคุณเป็นชาวโปรตุเกส คุณไม่ควรมีปัญหาใด ๆ เลยหากคุณกำลังจัดการกับข้อความภาษาโปรตุเกสเท่านั้น

อย่างไรก็ตามคุณไม่ต้องการลบสำเนียงออกไปฉันพนันได้เลย แต่คุณต้องการที่จะจับคู่สิ่งที่“ ไม่เน้นเสียง” ใช่ไหม? ถ้าเป็นเช่นนั้นคุณสามารถทำได้โดยใช้ICU4J (ICU สำหรับ Java) ระดับ หากคุณเปรียบเทียบที่จุดแข็งหลักเครื่องหมายเน้นเสียงจะไม่นับ ฉันทำสิ่งนี้ตลอดเวลาเพราะฉันมักประมวลผลข้อความภาษาสเปน ฉันมีตัวอย่างวิธีการทำเช่นนี้สำหรับคนสเปนที่นั่งอยู่ที่นี่ถ้าคุณต้องการ


ดังนั้นฉันต้องถือว่าวิธีการที่ให้ไว้ทั่วทั้งเว็บ (และแม้แต่ที่ SO) ไม่ใช่วิธีที่แนะนำสำหรับคำ "DeAccent" ฉันทำตรงสำหรับโปรตุเกส แต่เห็นวิธีการแปลก ๆ นี้ (และอย่างที่คุณพูดมันใช้ได้กับจุดประสงค์ของฉัน แต่วิธีสุดท้ายของฉันก็ทำ!) ดังนั้นมีแนวทางที่ "ใช้งานได้ดี" ที่ดีกว่าซึ่งจะครอบคลุมสถานการณ์ส่วนใหญ่หรือไม่ ตัวอย่างจะดีมาก ขอบคุณที่สละเวลา.
marcolopes

1
@Marcolopes: ฉันได้ปล่อยให้ข้อมูลยังคงอยู่และใช้ Unicode Collation Algorithm เพื่อทำการเปรียบเทียบความแข็งแกร่งหลัก วิธีนี้จะเป็นการเปรียบเทียบตัวอักษร แต่ไม่สนใจทั้งตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ นอกจากนี้ยังปล่อยให้สิ่งที่ควรเป็นตัวอักษรเดียวกันเป็นตัวอักษรเดียวกันซึ่งการลบสำเนียงเป็นเพียงการประมาณที่ซีดและไม่น่าพอใจ นอกจากนี้ยังไม่ต้องใช้ข้อมูลหากคุณสามารถใช้งานได้ในลักษณะที่ทำในสิ่งที่คุณต้องการ แต่ไม่ต้องการสิ่งนั้น
tchrist

คำตอบที่ดีทีเดียวคำถามหนึ่งคำถามฉันสามารถใช้ Normalizer ใน java และใช้ InCombiningDiacriticalMarks ได้หรือไม่ แต่ไม่รวมอักขระบางตัวเช่นüจากการแปลงเป็น u?
AlexCon

6
ใช่ฉันเข้าใจทั้งหมดนี้ทั้งหมด
Dónal

4

ใช้เวลาสักพัก แต่ฉันก็ทำมันให้หมด:

นี่คือ regexที่ควรรวมตัวอักษร zalgo ทั้งหมดรวมทั้งตัวที่ข้ามในช่วง 'ปกติ'

([\u0300–\u036F\u1AB0–\u1AFF\u1DC0–\u1DFF\u20D0–\u20FF\uFE20–\uFE2F\u0483-\u0486\u05C7\u0610-\u061A\u0656-\u065F\u0670\u06D6-\u06ED\u0711\u0730-\u073F\u0743-\u074A\u0F18-\u0F19\u0F35\u0F37\u0F72-\u0F73\u0F7A-\u0F81\u0F84\u0e00-\u0eff\uFC5E-\uFC62])

หวังว่านี่จะช่วยคุณประหยัดเวลาได้บ้าง

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