Perbedaan antara [0-9], [[: digit:]] dan \ d

35

Dalam artikel Wikipedia tentang ekspresi reguler , tampaknya [[:digit:]]= [0-9]= \d.

Bagaimana keadaan di mana mereka tidak setara? Apa bedanya?

Setelah beberapa penelitian, saya pikir satu perbedaan adalah bahwa ekspresi braket [:expr:]tergantung lokal.

harbinn
sumber
3
Bukankah artikel Wikipedia yang Anda tautkan untuk menjawab pertanyaan Anda? Prosesor / mesin ekspresi reguler yang berbeda mendukung sintaksis yang berbeda untuk kelas karakter (antara lain).
igal
@igal wiki mengatakan ada perbedaan tetapi tidak memberikan banyak detail. Saya menanyakan detailnya, seperti isaac, kata thrig. Saya cukup tertarik pada perbedaan mereka dalam grep, sed, awk ... apakah versi GNU atau tidak.
Harbinn

Jawaban:

40

Ya, itu [[:digit:]]~ [0-9]~ \d(di mana ~ berarti aproksimat).
Dalam sebagian besar bahasa pemrograman (jika didukung) \d[[:digit:]](identik).
Ini \dkurang umum daripada [[:digit:]](tidak dalam POSIX tetapi dalam GNU grep -P).

Ada banyak digit di UNICODE , misalnya:

123456789 # Hindu-Arabic Angka arab
٠١٢٣٤٥٦٧٨٩ # ARABIC-INDIC
۰۱۲۳۴۵۶۷۸۹ # EXTENDED ARABIC-INDIC/PERSIAN
߀߁߂߃߄߅߆߇߈߉ # NKO DIGIT
०१२३४५६७८९ # DEVANAGARI

Semuanya dapat dimasukkan dalam [[:digit:]]atau \d.

Sebaliknya, [0-9]umumnya hanya angka ASCII 0123456789.


Ada banyak bahasa: Perl, Java, Python, C. Di mana [[:digit:]](dan \d) panggilan untuk makna yang diperluas. Misalnya, kode perl ini akan cocok dengan semua digit dari atas:

$ a='0123456789 ٠١٢٣٤٥٦٧٨٩ ۰۱۲۳۴۵۶۷۸۹ ߀߁߂߃߄߅߆߇߈߉ ०१२३४५६७८९'

$ echo "$a" | perl -C -pe 's/[^\d]//g;' ; echo
0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९

Yang setara dengan memilih semua karakter yang memiliki properti Unicode Numericdan digits:

$ echo "$a" | perl -C -pe 's/[^\p{Nd}]//g;' ; echo
0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९

Grep mana yang dapat mereproduksi (versi spesifik pcre mungkin memiliki daftar internal yang berbeda dari poin kode numerik dari Perl):

$ echo "$a" | grep -oP '\p{Nd}+'
0123456789
٠١٢٣٤٥٦٧٨٩
۰۱۲۳۴۵۶۷۸۹
߀߁߂߃߄߅߆߇߈߉
०१२३४५६७८९

Ubah ke [0-9] untuk melihat:

$ echo "$a" | grep -o '[0-9]\+'
0123456789

POSIX

Untuk POSIX BRE atau ERE tertentu:
Tidak \ddidukung (tidak dalam POSIX tetapi dalam GNU grep -P). [[:digit:]]diperlukan oleh POSIX agar sesuai dengan kelas karakter digit, yang pada gilirannya diwajibkan oleh ISO C untuk menjadi karakter 0 hingga 9 dan tidak ada yang lain. Jadi hanya dalam C locale semua [0-9], [0123456789], \ddan [[:digit:]]berarti persis sama. Tidak [0123456789]ada kemungkinan salah tafsir, [[:digit:]]tersedia dalam lebih banyak utilitas dan itu umum untuk berarti saja [0123456789]. Ini \ddidukung oleh beberapa utilitas.

Adapun [0-9], arti dari range range hanya didefinisikan oleh POSIX di C locale; di lokal lain mungkin berbeda (mungkin urutan codepoint atau urutan pemeriksaan atau sesuatu yang lain).

kerang

Beberapa implementasi mungkin memahami rentang untuk menjadi sesuatu yang berbeda dari pesanan ASCII biasa (ksh93 misalnya):

$ LC_ALL=en_US.utf8 ksh -c 'a="'"$a"'";echo "${a//[0-9]}"'
  ۹ ߀߁߂߃߄߅߆߇߈߉ ९

Dan itu adalah sumber bug yang menunggu untuk terjadi.

Ishak
sumber
Dalam praktiknya pada sistem POSIX, iswctype()dan BRE / ERE / wildcard dalam utilitas POSIX, [0-9] dan [[: digit:]] hanya cocok dengan 0123456789. Dan itu akan dibuat eksplisit dalam revisi standar berikutnya
Stéphane Chazelas
Saya tidak tahu bahwa perlitu \ddalam mode Unicode cocok dengan angka desimal dari skrip lain. Terima kasih untuk itu. Dengan PCRE, lihat (*UCP)seperti di GNU grep -Po '(*UCP)\d'atau grep -Po '(*UCP)[[:digit:]]untuk kelas yang didasarkan pada properti Unicode.
Stéphane Chazelas
Saya setuju bahwa [:digit:]sintaks akan menyarankan Anda ingin menggunakan pelokalan, itulah yang dianggap pengguna sebagai angka. Saya tidak pernah menggunakan [:digit:]karena dalam praktiknya sama dengan [0-9]dan dalam kasus apa pun, selalu saya ingin mencocokkan pada 0123456789, saya tidak pernah bermaksud untuk mencocokkan ٠١٢٣٤٥٦٧٨٩, dan saya tidak dapat memikirkan kasus penggunaan di mana orang ingin mencocokkan dengan angka desimal. dalam skrip apa pun dengan utilitas POSIX. Lihat juga diskusi saat ini tentang [:blank:]di zsh ML . Kelas-kelas karakter itu agak berantakan.
Stéphane Chazelas
13

Ini tergantung pada bagaimana Anda mendefinisikan angka; [0-9]cenderung hanya yang ASCII (atau mungkin sesuatu yang bukan ASCII atau superset ASCII tetapi 10 digit yang sama seperti di ASCII hanya dengan representasi bit yang berbeda (EBCDIC)); \ddi sisi lain bisa berupa digit biasa (versi lama dari Perl, atau versi modern dari Perl dengan /aflag ekspresi reguler diaktifkan) atau bisa juga merupakan pasangan Unicode \p{Digit}yang lebih merupakan seperangkat digit lebih besar daripada [0-9]atau /\d/acocok.

$ perl -E 'say "match" if 42 =~ m/\d/'
match
$ perl -E 'say "match" if "\N{U+09EA}" =~ m/\d/'
match
$ perl -E 'say "match" if "\N{U+09EA}" =~ m/\d/a'
$ perl -E 'say "match" if "\N{U+09EA}" =~ m/[0-9]/'
$ 

perldoc perlrecharclass untuk informasi lebih lanjut, atau baca dokumentasi untuk bahasa yang dimaksud untuk melihat bagaimana perilakunya.

Tapi tunggu, masih ada lagi! Lokal juga dapat memvariasikan apa yang \dcocok, sehingga \dbisa mencocokkan digit lebih sedikit daripada set Unicode lengkap seperti itu, dan (mudah-mudahan, biasanya) juga termasuk [0-9]. Ini mirip dengan perbedaan dalam C antara isdigit(3)( [0-9]) dan isnumber(3)( [0-9ditambah apa pun dari lokal).

Mungkin ada panggilan yang dapat dilakukan untuk mendapatkan nilai digit, meskipun bukan [0-9]:

$ perl -MUnicode::UCD=num -E 'say num(4)'
4
$ perl -MUnicode::UCD=num -E 'say num("\N{U+09EA}")'
4
$ 
thrig
sumber
Saya pikir isnumber()adalah hal yang BSD, setidaknya berdasarkan pada halaman manual sepertinya begitu
ilkkachu
Saya memang punya bias BSD, ya
thrig
Bendera / a adalah pembatas khusus untuk mengurangi daftar digit Unicode yang hanya cocok … pengubah / a dapat digunakan untuk memaksa \ d agar cocok dengan ASCII 0 hingga 9 .. Dengan demikian, ini dipaksa untuk mencocokkan persis sama dan hanya [0-9].
Isaac
5

Arti berbeda [0-9], [[:digit:]]dan \ddisajikan dalam jawaban lain. Di sini saya ingin menambahkan perbedaan dalam implementasi mesin regex.

            [[:digit:]]    \d
grep -E               ✓     ×
grep -P               ✓     ✓
sed                   ✓     ×
sed -E                ✓     ×

Jadi [[:digit:]]selalu berhasil , \dtergantung. Dalam manual grep disebutkan bahwa [[:digit:]]hanya ada 0-9di Clokal.

PS1: Jika Anda tahu lebih banyak, silakan rentangkan tabel.

PS2: GNU grep 3.1 dan GNU 4.4 digunakan untuk pengujian.

harbinn
sumber
2
1) Ada banyak versi grepdan sed, dengan perbedaan terbesar mungkin antara versi GNU vs yang lain. Jawaban ini mungkin lebih bermanfaat jika disebutkan versi mana grepdan sedmerujuknya. Atau apa sumber tabel itu, dalam hal ini. 2) tabel itu mungkin juga ditranskripsi ke teks, karena tidak mengandung apa pun yang mengharuskannya menjadi gambar
ilkkachu
@ilkkachu 1) GNU grep 3.1 dan GNU 4.4 terbaru digunakan untuk pengujian. 2) Saya tidak tahu cara membuat tabel. Tampaknya @ muru telah mengonversi tabel ke bentuk teks yang cantik.
harbinn
@harbinn Harap edit itu ke dalam jawaban Anda.
Dan D.
@DanD. info versi ditambahkan. thx untuk perhatian
harbinn
1
Perhatikan bahwa python built in remodule tidak mendukung [[: digit:]] tetapi add in library regexmendukungnya sehingga saya akan sedikit niggle di selalu berfungsi. Itu selalu berfungsi dalam situasi keluhan posix.
Steve Barnes
4

Perbedaan teoretis telah dijelaskan dengan cukup baik dalam jawaban lain, jadi tetap menjelaskan perbedaan praktis .

Berikut adalah beberapa kasus penggunaan yang lebih umum untuk mencocokkan angka:


Ekstraksi data sekali pakai

Seringkali, ketika Anda ingin mengelompokkan beberapa angka, angka-angka itu sendiri berada dalam file teks yang diformat dengan canggung. Anda ingin mengekstraknya untuk digunakan dalam program Anda. Anda mungkin dapat mengetahui format angka (dengan melihat file) dan lokal Anda saat ini, jadi tidak apa - apa untuk menggunakan salah satu formulir , selama itu menyelesaikan pekerjaan. \dmembutuhkan penekanan tombol paling sedikit, sehingga sangat umum digunakan.

Sanitasi input

Anda memiliki beberapa input pengguna yang tidak dipercaya (mungkin dari formulir web), dan Anda perlu memastikan itu tidak mengandung kejutan. Mungkin Anda ingin menyimpannya dalam bidang angka dalam basis data, atau menggunakan sebagai parameter perintah shell untuk dijalankan di server. Dalam hal ini, Anda benar-benar menginginkannya [0-9], karena ini adalah yang paling ketat dan dapat diprediksi.

Validasi data

Anda memiliki sedikit data yang tidak akan Anda gunakan untuk hal-hal yang "berbahaya", tetapi alangkah baiknya jika Anda tahu itu angka. Misalnya, program Anda memungkinkan pengguna untuk memasukkan alamat, dan Anda ingin menyorot kesalahan ketik yang mungkin jika input tersebut tidak berisi nomor rumah. Dalam hal ini, Anda mungkin ingin menjadi seluas mungkin, begitu [[:digit:]]juga cara untuk pergi.


Itu tampaknya menjadi tiga kasus penggunaan paling umum untuk pencocokan angka. Jika Anda pikir saya melewatkan yang penting, silakan berikan komentar.

Bas
sumber
pekerjaan bagus, Apakah masalah keamanan terkait, seperti ReDoS atau yang lain
frams