Por que o intervalo [01-12] não funciona como esperado?

91

Estou tentando usar o padrão de intervalo [01-12]no regex para corresponder a mm de dois dígitos, mas isso não funciona conforme o esperado.

DEACTIVATIONPRESCRIPTION.NET
fonte
8
Você está combinando personagens , não sequências de caracteres . Basicamente, você está combinando com 0, 1 a 1 e 2 (ou seja, 0, 1 e 2). Considere [a-z0-9]o seguinte:, corresponde a todas as letras minúsculas e a todos os dígitos, mas apenas como um único caractere.
Lasse V. Karlsen
fwiw Eu criei uma ferramenta javascript que cria um regex altamente otimizado a partir de duas entradas (min / max) github.com/jonschlinkert/to-regex-range
jonschlinkert
0 [1-9] | 1 [0-2] -> 0 | 1 | 2 -> [] s em uma regex denotam uma classe de caracteres. Se nenhum intervalo for especificado, ele implicitamente ors cada caractere.
Badri Gs
Você precisa combiná-lo com regex puro? Se não, você pode: 1.) simplesmente usar o \d+padrão, 2.) converter as strings correspondentes em números em seu código. e então, 3.) verifique o intervalo de números como if(num >= 0 && num <= 12){ /*do something*/ }. É muito mais rápido e flexível.
acegs

Respostas:

192

Você parece não ter entendido como a definição das classes de caracteres funciona no regex.

Para combinar com qualquer das cordas 01, 02, 03, 04, 05, 06, 07, 08, 09, 10,11 , ou 12, algo como isso funciona:

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

Referências


Explicação

Uma classe de caractere, por si só, tenta corresponder a um e exatamente um caractere da string de entrada. [01-12]realmente define [012], uma classe de caracteres que corresponde a um carácter de entrada contra qualquer um dos 3 caracteres 0, 1ou2 .

A -definição de intervalo vai de 1a 1, que inclui apenas 1. Por outro lado, algo como [1-9]inclui 1, 2, 3, 4, 5, 6, 7, 8,9 .

Iniciantes costumam cometer erros de definição de coisas como [this|that]. Isso não "funciona". Este personagem define definição [this|a], isto é, ele corresponde a um carácter de entrada contra qualquer de 6 caracteres de t, h, i, s, |ou a. Mais do que provável(this|that) é o que se pretende.

Referências


Como os intervalos são definidos

Portanto, é óbvio agora que um padrão como between [24-48] hoursnão "funciona". A classe de caracteres, neste caso, é equivalente a[248] .

Ou seja, -em uma definição de classe de caractere não define intervalo numérico no padrão. Os motores Regex não "entendem" realmente os números no padrão, com exceção da sintaxe de repetição finita (por exemplo, a{3,5}correspondências entre 3 e 5 a).

Em vez disso, a definição de intervalo usa a codificação ASCII / Unicode dos caracteres para definir intervalos. O caractere 0é codificado em ASCII como decimal 48; 9é 57. Assim, a definição de caractere [0-9]inclui todos os caracteres cujos valores estão entre o decimal 48 e 57 na codificação. Em vez de forma sensata, pelo projeto Estes são os personagens 0, 1, ..., 9.

Veja também


Outro exemplo: A a Z

Vamos dar uma olhada em outra definição de classe de personagem comum [a-zA-Z]

Em ASCII:

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

Isso significa que:

  • [a-zA-Z]e [A-Za-z]são equivalentes
  • Na maioria dos sabores, [a-Z] é provável que seja um intervalo de caracteres ilegal
    • porque a(97) é "maior que" que Z(90)
  • [A-z] é legal, mas também inclui estes seis caracteres:
    • [(91), \(92), ](93), ^(94), _(95), `(96)

Perguntas relacionadas

poligenelubrificantes
fonte
Para mim, estava procurando meses sem prefixar 0 se fosse um dígito. E eu usei isso ([1-9] | (1 [0-2])) e funciona.
Bunjeeb
2
Importante observar: se você achar que esta página deseja uma solução para seu intervalo numérico que tenha apenas um dígito antes de chegar às dezenas, 0[1-9]|1[0-2]não funcionará. Alterá-lo para a próxima etapa lógica [1-9]|1[0-2]não quer trabalhar por razões compreensíveis (Ele corresponde a 1apenas em 10, 11, e 12). Tive que usar \b(?:[0-9]|1[0-1])\bpara evitar isso. \bfaz com que o regex corresponda aos limites da palavra (ou, neste caso, número) ( ^& $não); os colchetes fazem o ou ( |) considerar o outro lado dele; e, por fim, ?:não é criar uma submatch com o uso dos colchetes.
user66001
@polygenelubricants: "1,2,3,4,5,6,7,8,9,10,17,18".match(/^(([1-9]|1[0-7])\,?)+$/g )Você pode me dizer por que essa regex JS corresponde a mais de 17?
edam
@edam - poligenelubrificantes poderiam, e eu também, mas então estaríamos respondendo um questi ... espera ... esta é uma pergunta que você está pedindo em um comentário ? Existem rulez neste site;) Faça uma pergunta se tiver uma nova pergunta. Os comentários são apenas para criticar e pedir esclarecimentos e para responder a eles.
robinCTS
1
@edam Oh, entendo. Você fez uma pergunta novamente uma hora depois. Isso é ótimo! No entanto, provavelmente seria uma boa ideia excluir seu comentário aqui.
robinCTS
24

Uma classe de caractere em expressões regulares, denotada pela [...]sintaxe, especifica as regras para corresponder a um único caractere na entrada. Como tal, tudo o que você escreve entre os colchetes especifica como combinar um único caractere .

Seu padrão [01-12]é , portanto, dividido da seguinte forma:

  • 0 - corresponde ao único dígito 0
  • ou, 1-1, corresponde a um único dígito no intervalo de 1 a 1
  • ou, 2, corresponde a um único dígito 2

Basicamente, você só está correspondendo a 0, 1 ou 2.

Para fazer a correspondência desejada, combinando dois dígitos, variando de 01 a 12 como números, você precisa pensar em como eles ficarão como texto.

Você tem:

  • 01-09 (ou seja, o primeiro dígito é 0, o segundo dígito é 1-9)
  • 10-12 (ou seja, o primeiro dígito é 1, o segundo dígito é 0-2)

Você terá que escrever uma expressão regular para isso, que pode ser parecida com esta:

  +-- 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

Observe que tentar combiná-los para obter uma expressão mais curta falhará, fornecendo correspondências positivas falsas para entrada inválida.

Por exemplo, o padrão [0-1][0-9]corresponderia basicamente aos números 00-19, o que é um pouco mais do que você deseja.

Tentei encontrar uma fonte definitiva para obter mais informações sobre classes de personagens, mas por enquanto tudo que posso dar é esta Google Query para Regex Character Classes . Esperançosamente, você encontrará mais informações para ajudá-lo.

Lasse V. Karlsen
fonte
9

Isso também funciona:

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

[1-9] corresponde a dígitos únicos entre 1 e 9

[0-1][0-2] corresponde a dois dígitos entre 10 e 12

Existem alguns bons exemplos aqui

codingbadger
fonte
2
Para ser exato, [0-1][0-2]também corresponde 00. Dito isso, +1 para o link (que usei na minha resposta).
poligenelubrificantes
2
[0-1][0-2]devem ser interpretados com cautela, uma vez que permite cordas gosto 00, 01e 02, mas não admite 03até 09, admitindo finalmente 10, 11e 12. Um regex correto para isso é [1-9]|1[0-2], ou mesmo 0*([1-9]|1[0-2])(este último permitindo qualquer número de zeros à esquerda).
Luis Colorado
1

Os []s em uma regex denotam uma classe de caracteres . Se nenhum intervalo for especificado, ele implicitamente ou todos os caracteres dentro dele juntos. Portanto, [abcde]é o mesmo que (a|b|c|d|e), exceto que não captura nada; ele irá corresponder a qualquer um dos a, b, c, d, ou e. Tudo o que um intervalo indica é um conjunto de caracteres ; [ac-eg]diz "corresponde a qualquer um de a:; qualquer caractere entre ce e; ou g". Assim, sua correspondência diz "corresponder a qualquer um de 0:; qualquer caractere entre 1e 1( ou seja , apenas 1); ou2 .

Seu objetivo é evidentemente especificar um intervalo de números: qualquer número entre 01e 12escrito com dois dígitos. Neste caso específico, você pode combiná-lo com 0[1-9]|1[0-2]: a 0seguido por qualquer dígito entre 1e 9, ou a 1seguido por qualquer dígito entre 0e 2. Em geral, você pode transformar qualquer intervalo de números em uma regex válida de maneira semelhante. Pode haver uma opção melhor do que expressões regulares, no entanto, ou uma função ou módulo existente que pode construir a regex para você. Depende do seu idioma.

Antal Spector-Zabusky
fonte
0

Como os poligenelubrificantes dizem, o seu procuraria 0 | 1-1 | 2 em vez do que você deseja, devido ao fato de que as classes de caracteres (coisas em []) correspondem a caracteres em vez de strings.

fbstj
fonte
3
0|1-1|2- esta notação é muito enganosa. Algo como 0|1|2seria mais preciso.
poligenelubrificantes
0

Usa isto:

0?[1-9]|1[012]
  • 07: válido
  • 7: válido
  • 0: não corresponde
  • 00: não corresponde
  • 13: não corresponde
  • 21: não corresponde

Para testar um padrão como 07/2018, use o seguinte:

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

(Intervalo de datas entre 01/2000 a 12/9999)

Eolia
fonte
Tenho tentado descobrir como fazer isso, mas para que a terceira condição de apenas 0 passe.
mkaatman