Regex Javascript Konkret untuk Karakter Beraksen (Diakritik)

166

Saya telah melihat Stack Overflow ( mengganti karakter .. eh , bagaimana JavaScript tidak mengikuti standar Unicode tentang RegExp , dll.) Dan belum benar-benar menemukan jawaban konkret untuk pertanyaan:

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

Saya memaksa sebuah bidang di UI agar sesuai dengan format: last_name, first_name (terakhir [koma spasi] pertama) , dan saya ingin memberikan dukungan untuk diakritik, tetapi jelas dalam JavaScript itu sedikit lebih sulit daripada bahasa / platform lain.

Ini adalah versi asli saya, sampai saya ingin menambahkan dukungan diakritik:

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

Saat ini saya sedang memperdebatkan satu dari tiga metode untuk menambah dukungan, yang semuanya telah saya uji dan bekerja (setidaknya sampai batas tertentu, saya tidak benar-benar tahu apa "tingkat" dari pendekatan kedua). Di sini mereka:

Secara eksplisit mendaftar semua karakter beraksen yang ingin saya terima valid (lumpuh dan terlalu rumit):


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àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ]+$/
  • Ini dengan benar mencocokkan nama belakang / depan dengan salah satu karakter beraksen yang didukung di accentedCharacters.

Pendekatan saya yang lain adalah menggunakan .kelas karakter, untuk memiliki ekspresi yang lebih sederhana:

var regex = /^.+,\s.+$/;
  • Ini akan cocok untuk apa saja, setidaknya dalam bentuk: something, something. Saya kira tidak apa-apa ...

Pendekatan terakhir, yang baru saja saya temukan mungkin lebih sederhana ...

/^[a-zA-Z\u00C0-\u017F]+,\s[a-zA-Z\u00C0-\u017F]+$/
  • Ini cocok dengan berbagai karakter unicode - diuji dan bekerja, meskipun saya tidak mencoba sesuatu yang gila, hanya hal-hal normal yang saya lihat di departemen bahasa kami untuk nama anggota fakultas.

Inilah kekhawatiran saya:

  1. Solusi pertama terlalu terbatas, dan ceroboh dan berbelit-belit pada saat itu. Itu perlu diubah jika saya lupa satu atau dua karakter, dan itu tidak terlalu praktis.
  2. Solusi kedua lebih baik, ringkas, tetapi mungkin lebih cocok daripada yang seharusnya. Saya tidak bisa menemukan dokumentasi nyata pada persis apa yang .cocok, hanya generalisasi "karakter apapun kecuali karakter baris baru" (dari meja di MDN ).
  3. Solusi ketiga tampaknya menjadi yang paling tepat, tetapi apakah ada gotcha? Saya tidak terlalu akrab dengan Unicode, setidaknya dalam prakteknya, tetapi melihat tabel kode / kelanjutan dari tabel itu , \u00C0-\u017Ftampaknya cukup solid, setidaknya untuk input yang saya harapkan.

    • Fakultas tidak akan mengirimkan formulir dengan nama mereka dalam bahasa asli mereka (misalnya, Arab, Cina, Jepang, dll.) Jadi saya tidak perlu khawatir tentang karakter yang tidak sesuai dengan karakter Latin

Jadi pertanyaan sebenarnya : Manakah dari tiga pendekatan ini yang paling cocok untuk tugas itu? Atau adakah solusi yang lebih baik?

Chris Cirefice
sumber
1
Tampaknya tidak ada alasan khusus untuk menggunakan regexps yang lebih rumit. Satu-satunya hal tentang solusi paling sederhana adalah, itu juga akan cocok dengan "sesuatu, sesuatu, sesuatu". Anda dapat menggunakan sesuatu seperti regex = /^[^,]+,\s[^,]+$/;untuk mencegahnya.
usr2564301
4
Sekilas, yang pertama tidak akan cocok dengan nama umum "O'Donnell, Chris" atau menggabungkan nama belakang dengan tanda hubung, atau beberapa nama belakang (dll). Lihat Programmer Falsehood Believe About Names untuk hampir semua kemungkinan perangkap.
usr2564301
" Yang .atom cocok apa-apa kecuali baris " sebenarnya cukup tepat :-)
Bergi
1
Jika mungkin bagi Anda untuk menggunakan perpustakaan tambahan, Anda dapat melihat jawaban saya di sini
stema
Jongware, saya sebenarnya baru saja membaca artikel itu ketika saya browsing SO untuk jawaban atas pertanyaan saya - saya juga benar-benar lupa tentang tanda hubung dan apostrof dan sejenisnya, saya lebih peduli untuk menjadikannya internasional pertama: P Saya senang Anda membawanya meskipun! Dan Stema, saya benar-benar melihat perpustakaan itu dan saya menghindari menggabungkan perpustakaan karena ini semua ada pada Google Apps Script - menggabungkan perpustakaan eksternal akan menjadi mimpi buruk, dan saya hanya akan menggunakannya (dalam hal ini) untuk satu bidang tertentu ... jenis berlebihan: P
Chris Cirefice

Jawaban:

275

Cara lebih mudah untuk menerima semua aksen adalah ini:

[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 [ ] ^ \ × ÷

Lihat https://unicode-table.com/en/ untuk karakter yang tercantum dalam urutan angka.

Maycow Moura
sumber
2
Ini berfungsi dengan baik, +1, tetapi bisakah Anda menjelaskan mengapa itu bekerja?
Pierre Henry
1
@PierreHenry the -mendefinisikan rentang, dan teknik ini mengeksploitasi urutan karakter di charset untuk menentukan rentang kontinu, membuat solusi super ringkas untuk masalah
Angad
8
tidakkah kecocokan ini akan menggarisbawahi (dan karakter non-kata lainnya antara Zdan a)?
jcuenod
21
Ini cocok dengan setidaknya karakter [,], ^, dan \, tidak ada yang harus disertakan.
Nate
2
Tidak berfungsi, beberapa karakter dalam rentang ini bukan karakter beraksen (U + 00D7 sebagai tanda multiplikasi misalnya) lihat ini: unicode-table.com/en
Jérémy Pouyet
39

Rentang aksen Latin \u00C0-\u017Ftidak cukup untuk basis data nama saya, jadi saya memperpanjang regex ke

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

Saya menambahkan blok kode ini ( \u00C0-\u024Ftermasuk tiga blok yang berdekatan sekaligus):

Perhatikan bahwa \u00C0-\u00FFsebenarnya hanya bagian dari Suplemen Latin-1 . Rentang itu melompati sinyal kontrol yang tidak patut dan semua simbol kecuali untuk multiply × yang ditempatkan dengan canggung \u00D7dan bagi ÷ \u00F7.

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

Jika Anda membutuhkan lebih banyak poin kode, Anda dapat menemukan rentang lebih banyak di Daftar karakter Unicode Wikipedia . Misalnya, Anda juga bisa menambahkan Latin Extended-C , D , dan E , tetapi saya meninggalkannya karena hanya sejarawan yang tampaknya tertarik pada mereka sekarang, dan set D dan E bahkan tidak merender dengan benar di browser saya.

Regex asli berhenti pada \u017Ftersumbat pada nama "Șenol". Menurut FontSpace's Unicode Analyzer , karakter pertama adalah \u0218, LATIN MODAL SURAT S DENGAN COMMA DI BAWAH. (Ya, itu biasanya dieja dengan cedilla-S \u015E, "Şenol." Tapi aku tidak terbang ke Turki untuk pergi memberitahunya, "Kau salah mengeja namamu!")

Chaim Leib Halbert
sumber
1
Setelah melihat blok latin tabel unicode , saya pikir Anda juga harus memasukkan \ u1e00- \ u1eff, jadi saya lakukan[a-zA-Z\u00c0-\u024f\u1e00-\u1eff]
cprcrack
18

Manakah dari tiga pendekatan ini yang paling cocok untuk tugas itu?

Tergantung pada tugas :-) Untuk mencocokkan dengan tepat semua karakter Latin dan versi beraksennya, rentang Unicode mungkin memberikan solusi terbaik. Mereka mungkin diperluas ke semua karakter non-spasi, yang bisa dilakukan menggunakan \Skelas karakter.

Saya memaksa bidang dalam UI agar sesuai dengan format: last_name, first_name(terakhir [koma spasi] terlebih dahulu)

Masalah paling mendasar yang saya lihat di sini bukanlah diakritik, tetapi spasi putih. Ada beberapa nama yang terdiri dari beberapa kata, misalnya untuk judul. Jadi, Anda harus menggunakan yang paling umum, yang memungkinkan segalanya kecuali koma yang membedakan pertama dari nama belakang:

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

Tetapi solusi kedua Anda dengan .kelas karakter sama baiknya, Anda hanya perlu peduli dengan beberapa komata.

Bergi
sumber
Hm, mungkin Anda benar. Saya mungkin terlalu rumit ... Bisakah Anda menjelaskan regex yang Anda berikan? Saya telah bekerja dengan regex untuk sementara waktu sekarang, tetapi hanya hal-hal dasar, dan benar-benar saya tidak tahu apa yang sebenarnya Anda lakukan! Ha
Chris Cirefice
Ini adalah kelas karakter yang dinegasikan - yang berarti "apa pun selain koma".
Bergi
Ah, jadi lebih mirip any_character_not_a_comma, any_character_not_a_comma? Itulah yang saya pikirkan ketika pertama kali membacanya, saya agak bingung ketika melihat tiga koma di sana.
Chris Cirefice
Ya persis. Maaf untuk kebingungan dengan yang hilang suntuk ruang putih ...
Bergi
1
@ MateoTibaquirá Anda dapat menyederhanakan [^\s]ke\S
Bergi
15

The XRegExp perpustakaan memiliki plugin bernama Unicode yang membantu menyelesaikan tugas-tugas seperti ini.

<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>

Itu disebutkan dalam komentar untuk pertanyaan, tetapi mudah untuk dilewatkan. Saya perhatikan hanya setelah saya mengirimkan jawaban ini.

duri
sumber
Bagus, ternyata saya tidak perlu regex pada unicode, tetapi pada polanya anything, anything. Ini akan bermanfaat bagi pembaca masa depan :)
Chris Cirefice
12

Bagaimana dengan ini?

/^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/
alchn
sumber
2
Tidak cocok Šš.
Gajus
5

Bagaimana dengan ini?

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

Ini akan cocok dengan setiap kata dengan karakter beraksen atau tidak.

Javier Pallarés
sumber
2
Tapi OP ingin mengizinkan karakter beraksen.
barbsan
3
/^[\pL\pM\p{Zs}.-]+$/u

Penjelasan:

  • \pL - Cocok dengan segala jenis huruf dari bahasa apa pun
  • \pM - Atches karakter yang dimaksudkan untuk dikombinasikan dengan karakter lain (misalnya aksen, umlaut, kotak terlampir, dll)
  • \p{Zs} - Cocok dengan karakter spasi putih yang tidak terlihat, tetapi tidak memakan ruang
  • u - Pola dan string subjek diperlakukan sebagai UTF-8

Tidak seperti regex yang diusulkan lainnya (seperti [A-Za-zÀ-ÖØ-öø-ÿ]), ini akan bekerja dengan semua karakter khusus bahasa, misalnya Ššdicocokkan dengan aturan ini, tetapi tidak dicocokkan oleh orang lain di halaman ini.

Sayangnya, aslinya JavaScript tidak mendukung kelas-kelas ini. Namun, Anda dapat menggunakan xregexp, mis

const XRegExp = require('xregexp');

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