Concrete Javascript Regex สำหรับอักขระเน้นเสียง (กำกับเสียง)


166

ผมมองในกองมากเกิน ( แทนที่ตัวอักษร .. เอ๊ะ , วิธี JavaScript ไม่เป็นไปตามมาตรฐาน Unicode เกี่ยวกับนิพจน์ทั่วไป , ฯลฯ ) และมีไม่ได้จริงๆพบคำตอบที่เป็นรูปธรรมเพื่อคำถาม:

How can JavaScript match for accented characters (those with diacritical marks)?

ฉันบังคับให้ฟิลด์ใน UI จับคู่รูปแบบ: last_name, first_name (สุดท้าย [คอมม่าสเปซ] ก่อน)และฉันต้องการให้การสนับสนุนนักกำกับเสียง แต่เห็นได้ชัดว่าใน JavaScript มันยากกว่าภาษา / แพลตฟอร์มอื่นเล็กน้อย

นี่เป็นรุ่นดั้งเดิมของฉันจนกว่าฉันจะต้องการเพิ่มการสนับสนุนการออกเสียง:

/^[a-zA-Z]+,\s[a-zA-Z]+$/

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

แสดงรายการอักขระเน้นเสียงทั้งหมดที่ฉันต้องการยอมรับว่าใช้ได้อย่างถูกต้อง (อ่อนแอและซับซ้อนเกินไป):


var accentedCharacters = "àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ";
// Build the full regex
var regex = "^[a-zA-Z" + accentedCharacters + "]+,\\s[a-zA-Z" + accentedCharacters + "]+$";
// Create a RegExp from the string version
regexCompiled = new RegExp(regex);
// regexCompiled = /^[a-zA-ZàèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ]+,\s[a-zA-ZàèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ]+$/
  • นี้ได้อย่างถูกต้องตรงกับที่ผ่าน / ชื่อแรกกับใด ๆ accentedCharactersของตัวละครที่เน้นการสนับสนุนใน

วิธีอื่นของฉันคือใช้.คลาสของตัวละครเพื่อให้แสดงออกได้ง่ายขึ้น:

var regex = /^.+,\s.+$/;
  • something, somethingนี้จะตรงกับเพียงเกี่ยวกับอะไรอย่างน้อยในรูปแบบของ: ไม่เป็นไรฉันคิดว่า ...

วิธีสุดท้ายที่ฉันเพิ่งพบอาจจะง่ายกว่า ...

/^[a-zA-Z\u00C0-\u017F]+,\s[a-zA-Z\u00C0-\u017F]+$/
  • มันตรงกับช่วงของอักขระยูนิโค้ด - ผ่านการทดสอบและการทำงานแม้ว่าฉันจะไม่ได้ลองอะไรที่บ้าคลั่ง แต่เป็นเรื่องปกติที่ฉันเห็นในแผนกภาษาของเราสำหรับชื่ออาจารย์

นี่คือความกังวลของฉัน:

  1. ทางออกแรกคือการ จำกัด ที่มากเกินไปและเลอะเทอะและซับซ้อนที่ มันจะต้องเปลี่ยนถ้าฉันลืมตัวละครหนึ่งหรือสองตัวและนั่นก็ไม่เชิงปฏิบัติ
  2. วิธีแก้ปัญหาที่สองนั้นดีกว่ากระชับ แต่ก็อาจจะตรงกว่าที่ควรจะเป็น ฉันไม่สามารถหาเอกสารจริงใด ๆ เกี่ยวกับสิ่งที่.ตรงกันเพียงแค่ลักษณะทั่วไปของ "ตัวละครใด ๆ ยกเว้นตัวอักษรขึ้นบรรทัดใหม่" (จากตารางในMDN )
  3. วิธีที่สามดูเหมือนจะแม่นยำที่สุด แต่มี gotchas บ้างไหม? ฉันไม่คุ้นเคยกับ Unicode อย่างน้อยในทางปฏิบัติ แต่การดูตารางรหัส / ความต่อเนื่องของตารางนั้น\u00C0-\u017Fดูเหมือนจะค่อนข้างแข็งทีเดียวอย่างน้อยก็สำหรับสิ่งที่ฉันคาดหวัง

    • คณะจะไม่ส่งแบบฟอร์มพร้อมชื่อของพวกเขาเป็นภาษาแม่ (เช่นอาหรับ, จีน, ญี่ปุ่น, ฯลฯ ) ดังนั้นฉันจึงไม่ต้องกังวลเกี่ยวกับตัวละครนอกชุดอักขระละติน

ดังนั้นคำถามจริง : วิธีใดในสามแนวทางนี้ที่เหมาะสมที่สุดสำหรับงาน หรือมีวิธีแก้ปัญหาที่ดีกว่า


1
ดูเหมือนจะไม่มีเหตุผลใดที่จะใช้ regexps ที่ซับซ้อนมากขึ้น สิ่งเดียวที่เกี่ยวกับโซลูชันที่ง่ายที่สุดคือมันจะจับคู่กับ "บางสิ่งบางอย่างบางอย่าง" คุณสามารถใช้สิ่งที่ต้องการregex = /^[^,]+,\s[^,]+$/;ป้องกันได้
usr2564301

4
อย่างรวดเร็วคนแรกจะไม่ตรงกับชื่อสามัญ "O'Donnell, Chris" หรือรวมนามสกุลกับยัติภังค์หรือชื่อนามสกุล (ฯลฯ ) ดูผู้เขียนโปรแกรม Falsehoods เชื่อเกี่ยวกับชื่อสำหรับทุกข้อผิดพลาดที่เป็นไปได้
usr2564301

" อะตอมตรงกับสิ่งใดนอกจากการขึ้นบรรทัดใหม่ " จริงค่อนข้างแน่นอน :-).
Bergi

1
หากคุณสามารถใช้ห้องสมุดเพิ่มเติมคุณสามารถดูคำตอบของฉันที่นี่
stema

Jongware ฉันเพิ่งอ่านบทความนั้นในขณะที่ฉันกำลังสืบค้น SO เพื่อหาคำตอบสำหรับคำถามของฉัน - ฉันก็ลืมยัติภังค์และอะพอสโทรฟีอย่างสมบูรณ์เช่นกันฉันรู้สึกกังวลเกี่ยวกับการทำให้เป็นสากลก่อน: P ฉันดีใจที่คุณนำมันมา ขึ้นมา! และ Stema จริง ๆ แล้วฉันดูที่ห้องสมุดนั้นและฉันหลีกเลี่ยงการรวมไลบรารีเพราะนี่คือทั้งหมดใน Google Apps Script - การรวมไลบรารีภายนอกจะเป็นฝันร้ายและฉันจะใช้มันเท่านั้น (ในกรณีนี้) สำหรับเขตข้อมูลหนึ่ง ... overkill ชนิด: P
Chris Cirefice

คำตอบ:


275

วิธีที่ง่ายกว่าในการยอมรับสำเนียงทั้งหมดคือ:

[A-zÀ-ú] // accepts lowercase and uppercase characters
[A-zÀ-ÿ] // as above but including letters with an umlaut (includes [ ] ^ \ × ÷)
[A-Za-zÀ-ÿ] // as above but not including [ ] ^ \
[A-Za-zÀ-ÖØ-öø-ÿ] // as above but not including [ ] ^ \ × ÷

ดูhttps://unicode-table.com/en/สำหรับตัวอักษรที่อยู่ในลำดับตัวเลข


2
มันใช้งานได้ดี +1 แต่คุณช่วยอธิบายได้ไหมว่าทำไมมันถึงใช้ได้
ปิแอร์เฮนรี

1
@PierreHenry -กำหนดช่วงและเทคนิคนี้ใช้ประโยชน์จากการเรียงลำดับของตัวละครในชุดอักขระเพื่อกำหนดช่วงอย่างต่อเนื่องทำให้การแก้ปัญหาที่รัดกุมเป็นพิเศษ
Angad

8
การจับคู่นี้จะไม่เน้น (และตัวละครที่ไม่ใช่คำอื่นระหว่างZและa)
jcuenod

21
สิ่งนี้จะตรงกับตัวอักษรอย่างน้อย [,], ^ และ \, ซึ่งไม่ควรรวม
เนท

2
ไม่ทำงานอักขระบางตัวในช่วงนี้ไม่เน้นอักขระ (U + 00D7 เป็นเครื่องหมายการคูณ) ดูได้ที่: unicode-table.com/en
Jérémy Pouyet

39

ช่วงสำเนียงละติน\u00C0-\u017Fนั้นไม่เพียงพอสำหรับฐานข้อมูลชื่อของฉันดังนั้นฉันจึงขยาย regex ไป

[a-zA-Z\u00C0-\u024F]
[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF] // includes even more Latin chars

ฉันเพิ่มบล็อคโค้ดเหล่านี้ ( \u00C0-\u024Fรวมบล็อกที่อยู่ติดกันสามบล็อกพร้อมกัน):

ทราบว่า\u00C0-\u00FFเป็นจริงเพียงส่วนหนึ่งของLatin-1 เสริม ช่วงที่ข้ามสัญญาณ unprintable ควบคุมและสัญลักษณ์ทั้งหมดยกเว้นเชื่องช้าวางคูณ× และหาร÷\u00D7\u00F7

[a-zA-Z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u024F] // exclude ×÷

หากคุณจำเป็นต้องจุดรหัสอื่น ๆ อีกมากมายที่คุณสามารถหาช่วงเพิ่มเติมเกี่ยวกับวิกิพีเดียรายชื่อตัวละคร Unicode ตัวอย่างเช่นคุณสามารถเพิ่มLatin Extended-C , DและEได้ แต่ฉันทิ้งไว้เพราะมีเพียงนักประวัติศาสตร์ที่สนใจพวกเขาเท่านั้นในตอนนี้และชุด D และ E นั้นไม่ได้แสดงผลอย่างถูกต้องในเบราว์เซอร์ของฉัน

regex ดั้งเดิมหยุดที่\u017Fborked ในชื่อ "Șenol" จากการวิเคราะห์ Unicode ของ FontSpaceตัวอักษรตัวแรกนั้นคือ\u0218LATIN CAPITAL ตัวอักษร S พร้อมด้วย COMMA ด้านล่าง (ใช่มันมักจะสะกดด้วย cedilla-S \u015E"Şenol" แต่ฉันไม่ได้บินไปตุรกีเพื่อบอกเขาว่า "คุณสะกดชื่อผิด!")


1
เมื่อดูที่บล็อกละตินตารางยูนิโคดฉันคิดว่าคุณควรรวม \ u1e00- \ u1eff ด้วยดังนั้นฉันกำลังทำ[a-zA-Z\u00c0-\u024f\u1e00-\u1eff]
cprcrack

18

วิธีใดในสามวิธีนี้ที่เหมาะสมที่สุดสำหรับงาน

ขึ้นอยู่กับภารกิจ :-) เพื่อให้ตรงกับตัวอักษรละตินทั้งหมดและรุ่นที่เน้นเสียงช่วง Unicode อาจเป็นทางออกที่ดีที่สุด พวกเขาอาจขยายไปยังอักขระที่ไม่ใช่ whitespace ทั้งหมดซึ่งสามารถทำได้โดยใช้\Sคลาสอักขระ

ฉันกำลังบังคับให้ฟิลด์ใน UI เพื่อให้ตรงกับรูปแบบ: last_name, first_name(สุดท้าย [คอมม่าสเปซ] ก่อน)

ปัญหาพื้นฐานที่สุดที่ฉันเห็นที่นี่ไม่ใช่การออกเสียง แต่เป็นช่องว่าง มีบางชื่อที่ประกอบด้วยหลายคำเช่นสำหรับชื่อเรื่อง ดังนั้นคุณควรไปกับสามัญที่สุดที่อนุญาตทุกอย่างยกเว้นเครื่องหมายจุลภาคที่แยกความแตกต่างจากนามสกุล:

/[^,]+,\s[^,]+/

แต่ทางออกที่สองของคุณที่มี.คลาสตัวละครนั้นดีคุณอาจต้องใส่ใจกับคอมมาหลาย ๆ อัน


อืมบางทีคุณพูดถูก ฉันอาจจะซับซ้อนเกินไป ... คุณช่วยอธิบาย regex ที่คุณให้ไว้ได้ไหม? ฉันทำงานกับ regex มาระยะหนึ่งแล้ว แต่มีเพียงสิ่งพื้นฐานเท่านั้นและจริง ๆ แล้วฉันไม่มีเงื่อนงำสิ่งที่คุณทำจริง ๆ ! ฮา
Chris Cirefice

มันเป็นคลาสของตัวละครที่ถูกทำให้ไร้ผล - หมายถึง "อะไรก็ได้นอกเหนือจากเครื่องหมายจุลภาค"
Bergi

อ่ามันอ่านมากกว่านี้any_character_not_a_comma, any_character_not_a_commaเหรอ? นั่นคือสิ่งที่ฉันคิดว่าเมื่อฉันอ่านครั้งแรกฉันรู้สึกสับสนเมื่อเห็นคอมม่าสามลูกในนั้น
Chris Cirefice

ใช่แน่นอน ขออภัยสำหรับความสับสนที่หายไปsสำหรับช่องว่าง ...
Bergi

1
@ MateoTibaquiráคุณสามารถลดความซับซ้อนของ[^\s]การ\S
Bergi

15

XRegExpห้องสมุดมีปลั๊กอินที่ชื่อว่า Unicodeที่จะช่วยแก้ปัญหางานเช่นนี้

<script src="xregexp.js"></script>
<script src="addons/unicode/unicode-base.js"></script>
<script>
  var unicodeWord = XRegExp("^\\p{L}+$");

  unicodeWord.test("Русский"); // true
  unicodeWord.test("日本語"); // true
  unicodeWord.test("العربية"); // true
</script>

มันถูกกล่าวถึงในข้อคิดเห็นของคำถาม แต่พลาดง่าย ฉันสังเกตเห็นมันหลังจากที่ฉันส่งคำตอบนี้เท่านั้น


ดีปรากฎว่าฉันไม่จำเป็นต้องจริง regex ใน Unicode anything, anythingแต่ในรูปแบบ นี้จะเป็นประโยชน์สำหรับผู้อ่านในอนาคต :)
คริส Cirefice


5

แล้วเรื่องนี้ล่ะ

^([a-zA-Z]|[à-ú]|[À-Ú])+$

มันจะจับคู่ทุกคำด้วยอักขระเน้นเสียงหรือไม่


2
แต่ OP ต้องการอนุญาตอักขระเน้นเสียง
barbsan


3
/^[\pL\pM\p{Zs}.-]+$/u

คำอธิบาย:

  • \pL - จับคู่ตัวอักษรทุกชนิดจากภาษาใดก็ได้
  • \pM - สร้างตัวละครที่ตั้งใจจะรวมกับตัวละครอื่น (เช่นสำเนียง, เครื่องหมายบน, กล่องล้อมรอบ ฯลฯ )
  • \p{Zs} - จับคู่อักขระช่องว่างที่มองไม่เห็น แต่ใช้พื้นที่ว่าง
  • u - สตริงรูปแบบและหัวเรื่องถูกใช้เป็น UTF-8

ไม่เหมือนกับ regex อื่น ๆ ที่เสนอ (เช่น[A-Za-zÀ-ÖØ-öø-ÿ]) วิธีนี้จะใช้ได้กับอักขระเฉพาะภาษาทั้งหมดเช่นŠšถูกจับคู่กับกฎนี้ แต่ไม่ตรงกับผู้อื่นในหน้านี้

น่าเสียดายที่ JavaScript ไม่สนับสนุนคลาสเหล่านี้ อย่างไรก็ตามคุณสามารถใช้xregexpเช่น

const XRegExp = require('xregexp');

const isInputRealHumanName = (input: string): boolean => {
  return XRegExp('^[\\pL\\pM-]+ [\\pL\\pM-]+$', 'u').test(input);
};

1

คุณสามารถลบเครื่องหมายกำกับเสียงออกจากตัวอักษรโดยใช้:

var str = "résumé"`
str.normalize('NFD').replace(/[\u0300-\u036f]/g, '')` // returns resume

มันจะลบเครื่องหมายกำกับออกเสียงทั้งหมดแล้วดำเนินการ regex ของคุณบนมัน

อ้างอิง:

https://thread.engineering/2018-08-29-searching-and-sorting-text-with-diacritical-marks-in-javascript/

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