web-dev-qa-db-ja.com

[0-9]、[[:digit:]]、\ dの違い

Wikipediaの正規表現に関する記事 では、[[:digit:]] = [0-9] = \d

それらが等しくない状況は何ですか?違いはなんですか?

いくつか調査した結果、1つの違いはブラケット式[:expr:]はロケールに依存します。

37
harbinn

はい、それは[[:digit:]][0-9]\dです(ここで〜は概算を意味します)。
ほとんどのプログラミング言語(サポートされている場合)\d[[:digit:]](同一)。
\d[[:digit:]]より一般的ではありません(POSIXにはありませんが、GNU grep -Pにあります)。

NICODEの多くの数字 があります。次に例を示します。

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

すべて 含まれる場合があります[[:digit:]]または\d内。

代わりに、[0-9]は通常、ASCII digits 0123456789のみです。


Perl、Java、Python、Cなど、多くの言語があります。ここで、[[:digit:]](および\d)は拡張された意味を要求します。たとえば、次のPerlコードは上記のすべての数字と一致します。

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

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

これは、NumericおよびdigitsのUnicodeプロパティを持つすべての文字を選択することと同じです。

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

どのgrepが再現できるか(pcreの特定のバージョンには、Perlとは異なる数値コードポイントの内部リストがある場合があります):

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

それを[0-9]に変更して、以下を確認します。

$ echo "$a" | grep -o '[0-9]\+'
0123456789
٠١٢٣٤٥٦٧٨
۰۱۲۳۴۵۶۷۸
߀߁߂߃߄߅߆߇߈
०१२३४५६७८

POSIX

特定のPOSIX BREまたはEREの場合:
\dはサポートされていません(POSIXにはありませんが、GNU grep -Pにあります)。[[:digit:]]は、POSIXで対応するために必要です数字の文字クラス。これは、ISO Cで0から9までの文字である必要があり、それ以外は何も必要ないため、Cロケールでのみすべて[0-9][0123456789]\d[[:digit:]]はまったく同じ意味です。[0123456789]には誤解の可能性はありません。[[:digit:]]は他のユーティリティで使用できます。 [0123456789]のみを意味するのが一般的です。\dはいくつかのユーティリティでサポートされています。

[0-9]と同様に、範囲式の意味はCロケールのPOSIXによってのみ定義されます。他のロケールでは異なる場合があります(コードポイント順または照合順などの場合があります)。

シェル

一部の実装では、範囲を単純なASCII順序(ksh93など))とは異なるものとして理解する場合があります。

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

そして、それは発生するのを待っているバグの確かな原因です。

42
Isaac

これは、数字の定義方法によって異なります。 [0-9]は、ASCII ones(またはおそらくASCIIでもASCIIのスーパーセットでもないASCII異なるビット表現の場合のみ(EBCDIC))と同じ10桁。一方、\dは、単なる数字(古いバージョンのPerl、または最新バージョンのPerl /a正規表現フラグが有効)または、\p{Digit}または[0-9]一致よりも数字のセットが大きい/\d/aのUnicode一致である可能性があります。

$ 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 詳細については、または問題の言語のドキュメントを参照して、動作を確認してください。

しかし、待ってください、まだまだあります!ロケールは、\dが一致するものも異なる場合があるため、\dは、完全なUnicodeセットよりも少ない桁数に一致する可能性があり、(おそらく、通常は)[0-9]も含まれます。これは、Cでのisdigit(3)[0-9])とisnumber(3)[0-9とロケールの他のすべての違い)の違いに似ています。

[0-9]でなくても、桁の値を取得するために呼び出される可能性があります。

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

他の回答では、[0-9][[:digit:]]\dの意味が異なります。ここでは、正規表現エンジンの実装に違いを追加したいと思います。

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

したがって、[[:digit:]]は常に機能します\dによって異なります。 grepのマニュアルでは、Cロケールでは[[:digit:]]0-9であると記載されています。

PS1:詳しくは、表を展開してください。

PS2:GNU grep 3.1およびGNU 4.4がテストに使用されます。

7
harbinn

理論的な違いはすでに他の回答でかなりよく説明されているので、実用的な違いを説明することは残ります。

次に、数字を照合するためのより一般的な使用例をいくつか示します。


ワンショットデータ抽出

多くの場合、いくつかの数値をクランチしたい場合、数値自体が扱いにくい形式のテキストファイルに含まれています。それらをプログラムで使用するために抽出したいとします。 (ファイルを見れば)数値形式と現在のロケールがわかるので、ジョブが実行される限り、任意の形式を使用しても問題ありませんです。 \d必要なキーストロークが最も少ないため、非常に一般的に使用されます。

入力消毒

信頼できないユーザー入力(おそらくWebフォームから)があり、サプライズが含まれていないことを確認する必要があります。それをデータベースの数値フィールドに格納したり、サーバーで実行するシェルコマンドのパラメーターとして使用したりすることができます。この場合、あなたは本当に欲しい[0-9]、これは最も制限があり予測可能なものだからです。

データ検証

「危険」なものには何も使用しないデータが少しありますが、それが数値であるかどうかを知っておくと便利です。たとえば、プログラムでユーザーが住所を入力できるようにし、入力に家の番号が含まれていない場合に、入力ミスの可能性を強調したいとします。この場合、おそらくできるだけ広くしたいので、[[:digit:]]を使用する方法です。


これらは、数字照合の最も一般的な3つの使用例のようです。重要なものを逃したと思われる場合は、コメントをドロップしてください。

5
Bass