¿Por qué el rango [01-12] no funciona como se esperaba?

91

Estoy tratando de usar el patrón de rango [01-12]en expresiones regulares para que coincida con dos dígitos mm, pero esto no funciona como se esperaba.

DESACTIVACIÓNPRESCRIPTION.NET
fuente
8
Estás haciendo coincidir caracteres , no secuencias de caracteres . Básicamente, estás haciendo coincidir 0, 1 a 1 y 2 (es decir, 0, 1 y 2). Considere esto: [a-z0-9]esto coincide con todas las letras minúsculas y todos los dígitos, pero solo como un solo carácter.
Lasse V. Karlsen
fwiw, creé una herramienta de JavaScript que crea una expresión regular altamente optimizada a partir de dos entradas (mínimo / máximo) github.com/jonschlinkert/to-regex-range
jonschlinkert
0 [1-9] | 1 [0-2] -> 0 | 1 | 2 -> [] s en una expresión regular denotan una clase de carácter. Si no se especifican rangos, implícitamente ordena cada carácter.
Badri Gs
¿Necesitas combinarlo con expresión regular pura? De lo contrario, puede: 1.) simplemente usar el \d+patrón, 2.) convertir las cadenas coincidentes en números en su código. y luego, 3.) verifique el rango de números como if(num >= 0 && num <= 12){ /*do something*/ }. Es mucho más rápido y flexible.
acegs

Respuestas:

192

Parece haber entendido mal cómo funciona la definición de clases de caracteres en expresiones regulares.

Para que coincida con cualquiera de las cadenas 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, o 12, algo así como funciona esto:

0[1-9]|1[0-2]

Referencias


Explicación

Una clase de caracteres, por sí misma, intenta hacer coincidir uno y exactamente un carácter de la cadena de entrada. [01-12]en realidad define [012], una clase de caracteres que coincide con un carácter de la entrada en contra de cualquiera de los 3 personajes 0, 1o 2.

La -definición de rango va de 1a 1, que incluye solo 1. Por otro lado, algo así como [1-9]incluye 1, 2, 3, 4, 5, 6, 7, 8, 9.

Los principiantes suelen cometer errores al definir cosas como [this|that]. Esto no "funciona". Este carácter define definición [this|a], es decir, coincide con un carácter de la entrada contra cualquiera de 6 caracteres t, h, i, s, |o a. Más que probable (this|that)es lo que se pretende.

Referencias


Cómo se definen los rangos

Entonces es obvio ahora que un patrón como between [24-48] hoursno "funciona". La clase de carácter en este caso es equivalente a [248].

Es decir, -en una clase de caracteres, la definición no define un rango numérico en el patrón. Los motores de expresiones regulares no "comprenden" realmente los números en el patrón, con la excepción de la sintaxis de repetición finita (por ejemplo, a{3,5}coincidencias entre 3 y 5 a).

En su lugar, la definición de rango utiliza codificación ASCII / Unicode de los caracteres para definir rangos. El carácter 0está codificado en ASCII como decimal 48; 9es 57. Por lo tanto, la definición de carácter [0-9]incluye todos los caracteres cuyos valores están entre el decimal 48 y 57 en la codificación. En vez con sensatez, por diseño Estos son los personajes 0, 1, ..., 9.

Ver también


Otro ejemplo: de la A a la Z

Echemos un vistazo a otra definición de clase de carácter común [a-zA-Z]

En ASCII:

  • A= 65, Z= 90
  • a= 97, z= 122

Esto significa que:

  • [a-zA-Z]y [A-Za-z]son equivalentes
  • En la mayoría de los sabores, [a-Z]es probable que sea un rango de caracteres ilegal.
    • porque a(97) es "mayor que" que Z(90)
  • [A-z] es legal, pero también incluye estos seis caracteres:
    • [(91), \(92), ](93), ^(94), _(95), `(96)

Preguntas relacionadas

poligenelubricantes
fuente
Para mí, estuve buscando meses sin prefijar 0 si es de un solo dígito. Y usé esto ([1-9] | (1 [0-2])) y funciona.
bunjeeb
2
Es importante tener en cuenta: si encuentra que esta página desea una solución para su rango de números que solo tiene un solo dígito antes de llegar a las decenas, 0[1-9]|1[0-2]no funcionará. Si lo cambia al siguiente paso lógico [1-9]|1[0-2]no funciona, ya sea por razones comprensibles (Coincide con el 1sólo en 10, 11y 12). Tuve que usar \b(?:[0-9]|1[0-1])\bpara evitar eso. \b's se asegura de que la expresión regular coincida con los límites de la palabra (o en este caso el número) ( ^& $no); los corchetes hacen que o ( |) consideren el otro lado; y finalmente ?:es no crear una subcoincidencia con el uso de los corchetes.
user66001
@polygenelubricants: "1,2,3,4,5,6,7,8,9,10,17,18".match(/^(([1-9]|1[0-7])\,?)+$/g )¿Puede decirme por qué esta expresión regular JS coincide con 17?
edam
@edam - polygenelubricants podrían, por lo que podrían I, pero entonces estaríamos respondiendo a un questi ... espera ... ¿es una cuestión que está pidiendo en un comentario ? Hay rulez en este sitio;) Haga una pregunta si tiene una nueva pregunta. Los comentarios son solo para criticar y pedir aclaraciones, y para responder a ellos.
robinCTS
1
@edam Oh, ya veo. Usted hizo re-pregunta como una pregunta de una hora más tarde. ¡Eso es genial! Sin embargo, probablemente sería una buena idea eliminar su comentario aquí.
robinCTS
24

Una clase de caracteres en expresiones regulares, denotada por la [...]sintaxis, especifica las reglas para hacer coincidir un solo carácter en la entrada. Como tal, todo lo que escribe entre corchetes especifica cómo hacer coincidir un solo carácter .

Su patrón, por [01-12]lo tanto , se desglosa de la siguiente manera:

  • 0 - coincide con el dígito 0
  • o, 1-1, coincidir con un solo dígito en el rango de 1 a 1
  • o, 2, coincidir con un solo dígito 2

Entonces, básicamente, todo lo que estás haciendo coincidir es 0, 1 o 2.

Para hacer la coincidencia que desee, haciendo coincidir dos dígitos, que van del 01 al 12 como números, debe pensar en cómo se verán como texto.

Tienes:

  • 01-09 (es decir, el primer dígito es 0, el segundo dígito es 1-9)
  • 10-12 (es decir, el primer dígito es 1, el segundo dígito es 0-2)

Luego tendrá que escribir una expresión regular para eso, que puede verse así:

  +-- a 0 followed by 1-9
  |
  |      +-- a 1 followed by 0-2
  |      |
<-+--> <-+-->
0[1-9]|1[0-2]
      ^
      |
      +-- vertical bar, this roughly means "OR" in this context

Tenga en cuenta que intentar combinarlos para obtener una expresión más corta fallará, al dar coincidencias falsas positivas para una entrada no válida.

Por ejemplo, el patrón [0-1][0-9]básicamente coincidiría con los números 00-19, que es un poco más de lo que desea.

Intenté encontrar una fuente definida para obtener más información sobre las clases de personajes, pero por ahora todo lo que puedo darte es esta Consulta de Google para clases de personajes Regex . Con suerte, podrá encontrar más información allí para ayudarlo.

Lasse V. Karlsen
fuente
9

Esto también funciona:

^([1-9]|[0-1][0-2])$

[1-9] coincide con dígitos de un solo dígito entre 1 y 9

[0-1][0-2] coincide con dígitos dobles entre 10 y 12

Hay algunos buenos ejemplos aquí.

codificador
fuente
2
Para ser exactos, [0-1][0-2]también coincide 00. Dicho esto, +1 para el enlace (que he usado en mi respuesta).
polygenelubricants
2
[0-1][0-2]debe interpretarse con cuidado, ya que permite cadenas como 00, 01y 02, pero no admite 03hasta 09, admitiendo finalmente 10, 11y 12. Una expresión regular correcta para eso es [1-9]|1[0-2], o incluso 0*([1-9]|1[0-2])(esta última permite cualquier número de ceros iniciales).
Luis Colorado
1

La []s en una expresión regular denota una clase de carácter . Si no se especifican rangos, implícitamente o todos los caracteres dentro de él juntos. Por lo tanto, [abcde]es igual que (a|b|c|d|e), excepto que no captura nada; que coincidirá con cualquiera de a, b, c, d, o e. Todo lo que indica un rango es un conjunto de caracteres ; [ac-eg]dice "coincidir con cualquiera de a:; cualquier carácter entre cy e; o g". Por lo tanto, su coincidencia dice "coincidir con cualquiera de 0:; cualquier carácter entre 1y 1( es decir , solo 1); o 2.

Evidentemente, su objetivo es especificar un rango de números: cualquier número entre 01y 12escrito con dos dígitos. En este caso específico, puede emparejarlo con 0[1-9]|1[0-2]: a 0seguido de cualquier dígito entre 1y 9, o 1seguido de cualquier dígito entre 0y 2. En general, puede transformar cualquier rango de números en una expresión regular válida de manera similar. Sin embargo, puede haber una opción mejor que las expresiones regulares o una función o módulo existente que pueda construir la expresión regular para usted. Depende de tu idioma.

Antal Spector-Zabusky
fuente
0

Como dice Polygenelubricants, el suyo buscaría 0 | 1-1 | 2 en lugar de lo que desea, debido al hecho de que las clases de caracteres (cosas en []) coinciden con caracteres en lugar de cadenas.

fbstj
fuente
3
0|1-1|2- esta notación es muy engañosa. Algo como 0|1|2sería más preciso.
polygenelubricants
0

Utilizar este:

0?[1-9]|1[012]
  • 07: válido
  • 7: válido
  • 0: no coincide
  • 00: no coincide
  • 13: no coincide
  • 21: no coincide

Para probar un patrón como 07/2018 use esto:

/^(0?[1-9]|1[012])\/([2-9][0-9]{3})$/

(Rango de fecha entre 01/2000 y 12/9999)

Eolia
fuente
He estado tratando de averiguar cómo hacer esto, pero para que pase la tercera condición de solo un 0.
mkaatman