Diferencia entre [0-9], [[: dígito:]] y \ d

35

En el artículo de Wikipedia sobre expresiones regulares , parece que [[:digit:]]= [0-9]= \d.

¿Cuáles son las circunstancias en las que no son iguales? ¿Cuál es la diferencia?

Después de investigar un poco, creo que una diferencia es que la expresión entre paréntesis [:expr:]depende de la configuración regional.

harbinn
fuente
3
¿No responde el artículo de Wikipedia que ha vinculado para responder a su pregunta? Los diferentes procesadores / motores de expresión regular admiten diferentes sintaxis para las clases de caracteres (entre otras cosas).
igal
@igal wiki dice que hay una diferencia pero no da muchos detalles. Estoy preguntando los detalles, algo así como Isaac, dijo thrig. Estoy bastante interesado en su diferencia en grep, sed, awk ... ya sea la versión GNU o no.
harbinn el

Respuestas:

40

Sí, es [[:digit:]]~ [0-9]~ \d(donde ~ significa aproximado).
En la mayoría de los lenguajes de programación (donde es compatible) \d[[:digit:]](idéntico).
El \des menos común que [[:digit:]](no en POSIX pero está en GNU grep -P).

Hay muchos dígitos en UNICODE , por ejemplo:

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

Todo lo cual puede estar incluido en [[:digit:]]o \d.

En cambio, [0-9]generalmente son solo los dígitos ASCII 0123456789.


Hay muchos lenguajes: Perl, Java, Python, C. En los cuales [[:digit:]](y \d) requiere un significado extendido. Por ejemplo, este código perl coincidirá con todos los dígitos de arriba:

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

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

Lo que equivale a seleccionar todos los caracteres que tienen las propiedades Unicode de Numericy digits:

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

Qué grep podría reproducirse (la versión específica de pcre puede tener una lista interna diferente de puntos de código numérico que Perl):

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

Cámbielo a [0-9] para ver:

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

POSIX

Para el POSIX BRE o ERE específico:
El \dno es compatible (no en POSIX pero está en GNU grep -P). [[:digit:]]POSIX requiere que se corresponda con la clase de caracteres de dígitos, que a su vez ISO C requiere que sean los caracteres del 0 al 9 y nada más. Así que sólo en C locale todo [0-9], [0123456789], \dy [[:digit:]]significan exactamente lo mismo. No [0123456789]tiene posibles interpretaciones erróneas, [[:digit:]]está disponible en más utilidades y es común que solo signifique [0123456789]. El \des compatible con pocas utilidades.

En cuanto a [0-9], el significado de las expresiones de rango solo está definido por POSIX en la configuración regional C; en otros entornos locales puede ser diferente (puede ser el orden de los puntos de código o el orden de clasificación u otra cosa).

conchas

Algunas implementaciones pueden entender que un rango es algo diferente del orden ASCII simple (por ejemplo, ksh93):

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

Y esa es una fuente segura de errores que esperan suceder.

Isaac
fuente
En la práctica en los sistemas POSIX, iswctype()y BRE / ERE / comodines en las utilidades POSIX, [0-9] y [[: digit:]] coinciden solo en 0123456789. Y eso se hará explícito en la próxima revisión de la norma
Stéphane Chazelas,
No sabía que perlel \dmodo Unicode coincidía con dígitos decimales de otros scripts. Gracias por eso. Con PCRE, vea (*UCP)como en GNU grep -Po '(*UCP)\d'o grep -Po '(*UCP)[[:digit:]]para que las clases se basen en propiedades Unicode.
Stéphane Chazelas
Estoy de acuerdo en que la [:digit:]sintaxis sugeriría que desea utilizar la localización, eso es lo que el usuario considere como un dígito. Nunca lo uso [:digit:]porque en la práctica es lo mismo [0-9]y, en cualquier caso, siempre quiero coincidir en 0123456789, nunca quiero hacerlo ٠١٢٣٤٥٦٧٨٩, y no puedo pensar en un caso de uso en el que uno quisiera coincidir en un dígito decimal en cualquier script con utilidades POSIX. Vea también la discusión actual sobre [:blank:]zsh ML . Esas clases de personajes son un poco desordenadas.
Stéphane Chazelas
13

Esto depende de cómo defina un dígito; [0-9]tiende a ser solo ASCII (o posiblemente algo más que no sea ASCII ni un superconjunto de ASCII sino los mismos 10 dígitos que en ASCII solo con diferentes representaciones de bits (EBCDIC)); \dpor otro lado, podría ser solo los dígitos simples (versiones antiguas de Perl o versiones modernas de Perl con el /aindicador de expresión regular habilitado) o podría ser una coincidencia Unicode, \p{Digit}que es más bien un conjunto de dígitos más grande que [0-9]o /\d/acoincidente.

$ 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 para obtener más información o consulte la documentación del idioma en cuestión para ver cómo se comporta.

¡Pero espera hay mas! La configuración regional también puede variar qué \dcoincide, por lo que \dpodría coincidir con menos dígitos que el conjunto Unicode completo de los mismos, y (con suerte, generalmente) también incluye [0-9]. Esto es similar a la diferencia en C entre isdigit(3)( [0-9]) y isnumber(3)( [0-9más cualquier otra cosa de la configuración regional).

Puede haber llamadas que se pueden hacer para obtener el valor del dígito, incluso si no es así [0-9]:

$ perl -MUnicode::UCD=num -E 'say num(4)'
4
$ perl -MUnicode::UCD=num -E 'say num("\N{U+09EA}")'
4
$ 
thrig
fuente
Creo que isnumber()es una cuestión de BSD, al menos según la página de manual que parece ser
ilkkachu
Tengo algo de un sesgo BSD, sí
thrig
El indicador / a es un limitador específico para reducir la lista de dígitos Unicode para que solo coincida ... el modificador / a se puede usar para forzar \ d para que coincida solo con el ASCII 0 a 9 .. Como tal, está obligando a coincidir exactamente igual y solo [0-9].
Isaac
5

Diferente significado de [0-9], [[:digit:]]y \dse presentan en otras respuestas. Aquí me gustaría agregar diferencias en la implementación del motor regex.

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

Así que [[:digit:]]siempre funciona , \ddepende. En el manual de grep se menciona que [[:digit:]]solo está 0-9en la Cconfiguración regional.

PS1: Si sabes más, expande la tabla.

PS2: GNU grep 3.1 y GNU 4.4 se utilizan para la prueba.

harbinn
fuente
2
1) Hay muchas versiones de grepy sed, con la mayor diferencia probablemente entre las versiones de GNU frente a otras. Esta respuesta podría ser más útil si menciona a qué versión grepy sedse refiere. O cuál es la fuente de esa tabla, para el caso. 2) esa tabla también podría transcribirse al texto, ya que no contiene nada que requiera que sea una imagen
ilkkachu
@ilkkachu 1) se utiliza la última versión de GNU grep 3.1 y GNU 4.4 para la prueba. 2) No sé cómo crear una tabla. Parece que @ muru ha convertido la tabla a una bonita forma de texto.
Harbinn
@harbinn Edítelo en su respuesta.
Dan D.
@DanD. la información de la versión agregada. gracias por la atención
harbinn
1
Tenga en cuenta que el remódulo incorporado de Python no admite [[: digit:]] pero la biblioteca de complementos regexsí lo admite, por lo que me molestaría un poco que siempre funcione. Siempre funciona en situaciones de queja posix.
Steve Barnes
4

Las diferencias teóricas ya se han explicado bastante bien en las otras respuestas, por lo que queda por explicar las diferencias prácticas .

Estos son algunos de los casos de uso más comunes para hacer coincidir un dígito:


Extracción de datos de una sola vez

A menudo, cuando desea agrupar algunos números, los números mismos se encuentran en un archivo de texto con un formato extraño. Desea extraerlos para usarlos en su programa. Probablemente pueda decir el formato de número (mirando el archivo) y su ubicación actual, por lo que está bien usar cualquiera de los formularios , siempre que haga el trabajo. \drequiere la menor cantidad de pulsaciones de teclas, por lo que se usa con mucha frecuencia.

Desinfección de entrada

Tiene una entrada de usuario no confiable (tal vez de un formulario web), y debe asegurarse de que no contenga ninguna sorpresa. Tal vez desee almacenarlo en un campo numérico en una base de datos, o usarlo como parámetro de un comando de shell para ejecutarlo en un servidor. En este caso, realmente quieres [0-9], ya que es el más restrictivo y predecible.

Validación de datos

Tiene un poco de información que no va a utilizar para nada "peligroso", pero sería bueno saber si es un número. Por ejemplo, su programa le permite al usuario ingresar una dirección, y usted desea resaltar un posible error tipográfico si la entrada no contiene un número de casa. En este caso, probablemente desee ser lo más amplio posible, por lo que [[:digit:]]es el camino a seguir.


Parecen ser los tres casos de uso más comunes para la coincidencia de dígitos. Si crees que me perdí uno importante, por favor deja un comentario.

Bajo
fuente
buen trabajo, está relacionado con un problema de seguridad, como Redos u otros
frams