Sự khác biệt giữa [0-9], [[: chữ số:]] và \ d


35

Trong bài viết Wikipedia về Biểu thức chính quy , có vẻ như [[:digit:]]= [0-9]= \d.

Những trường hợp mà họ không bằng nhau là gì? Sự khác biệt là gì?

Sau một số nghiên cứu, tôi nghĩ một điểm khác biệt là biểu thức ngoặc [:expr:]phụ thuộc vào miền địa phương.


3
Không phải bài viết Wikipedia mà bạn liên kết để trả lời câu hỏi của bạn? Các bộ xử lý biểu thức chính quy khác nhau hỗ trợ các cú pháp khác nhau cho các lớp ký tự (trong số những thứ khác).
igal

@igal wiki nói có sự khác biệt nhưng không cung cấp nhiều chi tiết. Tôi đang hỏi chi tiết, một cái gì đó giống như isaac, thrig nói. Tôi khá quan tâm đến sự khác biệt của chúng trong grep, sed, awk ... cho dù là phiên bản GNU hay không.
harbinn

Câu trả lời:


40

Vâng, đó là [[:digit:]]~ [0-9]~ \d(trong đó ~ có nghĩa là aproximate).
Trong hầu hết các ngôn ngữ lập trình (nơi nó được hỗ trợ) \d[[:digit:]](giống hệt nhau).
Điều \dnày ít phổ biến hơn [[:digit:]](không phải trong POSIX mà là trong GNU grep -P).

nhiều chữ số trong UNICODE , ví dụ:

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

Tất cả trong số đó có thể được bao gồm trong [[:digit:]]hoặc \d.

Thay vào đó, [0-9]nói chung chỉ là các chữ số ASCII 0123456789.


Có nhiều ngôn ngữ: Perl, Java, Python, C. Trong đó [[:digit:]](và \d) gọi một nghĩa mở rộng. Ví dụ: mã perl này sẽ khớp với tất cả các chữ số ở trên:

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

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

Tương đương với việc chọn tất cả các ký tự có thuộc tính Unicode Numericdigits:

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

Grep nào có thể sao chép (phiên bản cụ thể của pcre có thể có một danh sách nội bộ khác nhau về các điểm mã số so với Perl):

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

Thay đổi nó thành [0-9] để xem:

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

POSIX

Đối với POSIX BRE hoặc ERE cụ thể:
Không \dđược hỗ trợ (không phải trong POSIX mà là GNU grep -P). [[:digit:]]được POSIX yêu cầu tương ứng với lớp ký tự chữ số, lần lượt được ISO C yêu cầu là các ký tự từ 0 đến 9 và không có gì khác. Vì vậy, chỉ trong C Locale tất cả [0-9], [0123456789], \d[[:digit:]]có nghĩa là giống hệt nhau. Các [0123456789]không có người hiểu nhầm có thể, [[:digit:]]có sẵn trong tiện ích hơn và người ta thường có nghĩa là chỉ [0123456789]. Các \dđược hỗ trợ bởi vài tiện ích.

Đối với [0-9], ý nghĩa của các biểu thức phạm vi chỉ được xác định bởi POSIX trong miền địa phương C; ở các địa phương khác, nó có thể khác (có thể là thứ tự mã hóa hoặc thứ tự đối chiếu hoặc thứ gì khác).

vỏ sò

Một số triển khai có thể hiểu một phạm vi là một cái gì đó khác với thứ tự ASCII đơn giản (ví dụ ksh93):

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

Và đó là một nguồn chắc chắn của lỗi đang chờ xảy ra.


Trong thực tế trên các hệ thống POSIX iswctype()và BRE / ERE / ký tự đại diện trong các tiện ích POSIX, [0-9] và [[: chữ số:]] chỉ khớp trên 0123456789. Và điều đó sẽ được làm rõ trong bản sửa đổi tiếp theo của tiêu chuẩn
Stéphane Chazelas

Tôi đã không biết rằng perl\dtrong chế độ Unicode phù hợp trên chữ số thập phân từ các kịch bản khác. Cảm ơn vì điều đó. Với PCRE, xem (*UCP)như trong GNU grep -Po '(*UCP)\d'hoặc grep -Po '(*UCP)[[:digit:]]để biết các lớp dựa trên các thuộc tính Unicode.
Stéphane Chazelas

Tôi đồng ý rằng [:digit:]cú pháp sẽ gợi ý rằng bạn muốn sử dụng bản địa hóa, đó là bất cứ điều gì người dùng coi là một chữ số. Tôi không bao giờ sử dụng [:digit:]bởi vì trong thực tế, nó giống như [0-9]và trong mọi trường hợp, tôi luôn luôn khớp với số 0123456789, tôi không bao giờ có ý định khớp ٠١٢٣٤٥٦٧٨٩và tôi không thể nghĩ đến trường hợp sử dụng mà người ta muốn khớp với chữ số thập phân trong bất kỳ tập lệnh nào với các tiện ích POSIX. Xem thêm các cuộc thảo luận hiện tại về [:blank:]Zsh ML . Những lớp nhân vật là một chút lộn xộn.
Stéphane Chazelas

13

Điều này phụ thuộc vào cách bạn xác định một chữ số; [0-9]có xu hướng chỉ là các ASCII (hoặc có thể là một thứ khác không phải là ASCII cũng không phải là siêu ký tự của ASCII mà là 10 chữ số giống như trong ASCII chỉ với các cách biểu diễn bit khác nhau (EBCDIC)); \dmặt khác có thể chỉ là các chữ số đơn giản (các phiên bản cũ của Perl hoặc các phiên bản hiện đại của Perl với /acờ biểu thức thông thường được bật) hoặc có thể là một kết hợp Unicode \p{Digit}có nghĩa là một bộ chữ số lớn hơn so với [0-9]hoặc /\d/akhớp.

$ 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 để biết thêm thông tin hoặc tham khảo tài liệu về ngôn ngữ được đề cập để xem cách ứng xử.

Nhưng xin chờ chút nữa! Ngôn ngữ cũng có thể thay đổi những gì \dtrùng khớp, do đó \dcó thể khớp với ít chữ số hơn bộ Unicode hoàn chỉnh như vậy và (hy vọng là thường) cũng bao gồm [0-9]. Điều này tương tự như sự khác biệt về C giữa isdigit(3)( [0-9]) và isnumber(3)( [0-9cộng với bất cứ điều gì khác từ miền địa phương).

Có thể có các cuộc gọi có thể được thực hiện để lấy giá trị của chữ số, ngay cả khi nó không phải là [0-9]:

$ perl -MUnicode::UCD=num -E 'say num(4)'
4
$ perl -MUnicode::UCD=num -E 'say num("\N{U+09EA}")'
4
$ 

Tôi nghĩ isnumber()là một điều BSD, ít nhất là dựa trên trang người đàn ông có vẻ như vậy
ilkkachu

Tôi có cái gì đó của một sự thiên vị BSD, vâng
thrig

Cờ / a là một bộ giới hạn cụ thể để giảm danh sách các chữ số Unicode để chỉ khớp với bộ điều chỉnh / bộ sửa đổi có thể được sử dụng để buộc \ d khớp với chỉ ASCII 0 đến 9 .. Như vậy, nó buộc phải khớp chính xác và giống nhau [0-9].
Isaac

5

Ý nghĩa khác nhau của [0-9], [[:digit:]]\dđược trình bày trong các câu trả lời khác. Ở đây tôi muốn thêm sự khác biệt trong việc thực hiện công cụ regex.

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

Vì vậy, [[:digit:]]luôn luôn làm việc , \dphụ thuộc. Trong hướng dẫn của grep, nó được đề cập [[:digit:]]chỉ là 0-9Cmiền địa phương.

PS1: Nếu bạn biết thêm, vui lòng mở rộng bảng.

PS2: GNU grep 3.1 và GNU 4.4 được sử dụng để kiểm tra.


2
1) Có nhiều phiên bản grepsed, với sự khác biệt lớn nhất có lẽ là giữa các phiên bản GNU so với các phiên bản khác. Câu trả lời này có thể hữu ích hơn nếu nó đề cập đến phiên bản nào grepsednó đề cập đến. Hoặc nguồn của bảng đó là gì, cho vấn đề đó. 2) bảng đó cũng có thể được sao chép thành văn bản, vì nó không chứa bất cứ thứ gì đòi hỏi nó phải là một hình ảnh
ilkkachu

@ilkkachu 1) GNU grep 3.1 và GNU 4.4 mới nhất được sử dụng để kiểm tra. 2) Tôi không biết cách tạo bảng. Có vẻ như @ muru đã chuyển đổi bảng thành một dạng văn bản đẹp.
harbinn

@harbinn Vui lòng chỉnh sửa câu trả lời của bạn.
Dan D.

@DanD. thông tin phiên bản được thêm vào. thx cho sự chú ý
harbinn

1
Lưu ý rằng python được xây dựng trong remô-đun không hỗ trợ [[: chữ số:]] nhưng add in library regexkhông hỗ trợ nó nên tôi sẽ cố gắng một chút để luôn luôn hoạt động. Nó luôn hoạt động trong các tình huống khiếu nại posix.
Steve Barnes

4

Sự khác biệt về lý thuyết đã được giải thích khá rõ trong các câu trả lời khác, vì vậy vẫn còn để giải thích sự khác biệt thực tế .

Dưới đây là một số trường hợp sử dụng phổ biến hơn để khớp một chữ số:


Trích xuất dữ liệu một lần

Thông thường, khi bạn muốn crunch một số số, các số đó nằm trong một tệp văn bản được định dạng lúng túng. Bạn muốn trích xuất chúng để sử dụng trong chương trình của bạn. Bạn có thể có thể cho biết định dạng số (bằng cách xem tệp) và ngôn ngữ hiện tại của bạn, vì vậy bạn có thể sử dụng bất kỳ biểu mẫu nào , miễn là hoàn thành công việc. \dyêu cầu ít tổ hợp phím nhất, vì vậy nó được sử dụng rất phổ biến.

Vệ sinh đầu vào

Bạn có một số đầu vào người dùng không đáng tin cậy (có thể từ một biểu mẫu web) và bạn cần chắc chắn rằng nó không chứa bất kỳ sự ngạc nhiên nào. Có thể bạn muốn lưu trữ nó trong một trường số trong cơ sở dữ liệu hoặc sử dụng làm tham số cho lệnh shell để chạy trên máy chủ. Trong trường hợp này, bạn thực sự muốn [0-9], vì đó là điều hạn chế nhất và có thể dự đoán được.

Xác nhận dữ liệu

Bạn có một chút dữ liệu mà bạn sẽ không sử dụng cho bất cứ điều gì "nguy hiểm", nhưng thật tuyệt nếu biết đó có phải là một con số không. Ví dụ: chương trình của bạn cho phép người dùng nhập địa chỉ và bạn muốn đánh dấu một lỗi đánh máy có thể nếu đầu vào không chứa số nhà. Trong trường hợp này, bạn có thể muốn càng rộng càng tốt, đó [[:digit:]]là cách để đi.


Đó dường như là ba trường hợp sử dụng phổ biến nhất để khớp chữ số. Nếu bạn nghĩ rằng tôi đã bỏ lỡ một điều quan trọng, xin vui lòng để lại nhận xét.


công việc tốt, vấn đề bảo mật có liên quan, chẳng hạn như ReDoS hoặc những người khác
fram
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.