Motifs1 chasse jouable incluse

Regex pour les adresses IPv4 : bien couvrir les nombres de 0 à 255

Une adresse IPv4, c'est quatre nombres de 0 à 255. Voici pourquoi le motif évident \d{1,3} laisse passer 999.999.999.999, comment décrire un octet avec l'alternance, que faire des zéros en tête, et comment trouver des adresses dans du texte.

Logo de Regex Hunter

· 7 min de lecture · Mis à jour le

/^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/

à attraper

  • 0
  • 42
  • 199
  • 255

à épargner

  • 256
  • 300
  • 07
  • 1000

Une adresse IPv4, c'est quatre nombres séparés par des points, comme 192.168.1.1. Chaque nombre s'appelle un octet, et va de 0 à 255.

Ça semble facile à reconnaître, et le premier motif que la plupart des gens écrivent est court et faux. Cet article part de là, le corrige étape par étape, et termine par un motif pour trouver des adresses dans du texte. Tous les exemples sont interactifs : modifiez les motifs et regardez les lignes changer.

Réponse rapideJavaScript
/^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/

Quatre nombres de 0 à 255 séparés par des points, sans zéro en tête.

Accepte

  • 192.168.1.1
  • 10.0.0.1
  • 255.255.255.0

Rejette

  • 256.1.1.1
  • 010.0.0.1
  • 1.2.3

01Le motif évident

Quatre groupes de un à trois chiffres, séparés par des points :

Correspondances6/6 comme prévu
/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/

Doit correspondre4/4

  • 192.168.1.1
  • 8.8.8.8
  • 999.999.999.999
  • 300.1.2.3

Ne doit pas2/2

  • 1.2.3
  • 1.2.3.4.5
Essayez-le dans le bac à sable
La forme est bonne, mais \d{1,3} veut dire « n'importe quel nombre de 0 à 999 ». Les deux dernières lignes ne sont pas de vraies adresses.

Il repère bien un mauvais nombre de parties. Et si vous parcourez des logs auxquels vous faites confiance, cela suffit peut-être. Mais comme validateur, il accepte tout jusqu'à 999.

La répétition peut être plus courte. Trois octets se terminent chacun par un point : regroupez « octet plus point » et répétez-le trois fois avec {3} :

Motif23 car.
/^(\d{1,3}\.){3}\d{1,3}$/
Essayez-le dans le bac à sable
Le même motif que ci-dessus. Le groupe (\d{1,3}\.) apparaît trois fois, puis un dernier octet sans point après.

02N'oubliez pas la barre oblique inverse

Dans un motif, un . seul veut dire « n'importe quel caractère ». Oubliez les barres obliques inverses, et les points ne désignent plus des points :

Correspondances3/3 comme prévu
/^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$/
  • 1.2.3.4
  • 1a2b3c4
  • 1234567
Essayez-le dans le bac à sable
Les deux dernières lignes correspondent. Les points non échappés acceptent des lettres, et même des chiffres.

1234567 est le plus sournois. Chaque . correspond à un chiffre : le motif le lit comme 1, 3, 5, 7, avec 2, 4, 6 en guise de « points ».

03Un octet, de 0 à 255

Une regex n'a aucune idée de la valeur d'un nombre. Elle ne voit que des caractères. « De 0 à 255 » doit donc s'écrire sous forme de règles sur le texte, découpées en plages qui ont chacune une forme simple :

Plage Motif Se lit
250–255 25[0-5] 25, puis 0 à 5
200–249 2[0-4]\d 2, puis 0 à 4, puis n'importe quel chiffre
100–199 1\d\d 1, puis deux chiffres quelconques
0–99 [1-9]?\d un 1 à 9 facultatif, puis n'importe quel chiffre

Reliez-les avec | et entourez-les d'un groupe :

Correspondances10/10 comme prévu
/^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/

Doit correspondre6/6

  • 0
  • 9
  • 42
  • 199
  • 249
  • 255

Ne doit pas4/4

  • 256
  • 300
  • 07
  • 1000
Essayez-le dans le bac à sable
Chaque branche couvre une plage. 256 échoue sur les quatre : 25[0-5] s'arrête à 5, 2[0-4]\d exige 0 à 4 au milieu, 1\d\d exige un 1 en tête, et [1-9]?\d fait au plus deux chiffres.

04Les zéros en tête

Regardez la dernière branche : [1-9]?\d, et non \d{1,2}. C'est ce qui rejette 07 et 010. Un octet à deux chiffres doit commencer par 1 à 9.

C'est plus important qu'il n'y paraît. Certains outils lisent un zéro en tête comme un nombre octal. socket.inet_aton en Python, qui suit les vieilles règles réseau du C, transforme 010.0.0.1 en 8.0.0.1, car 10 en octal vaut 8. Si une partie de votre système lit 010 comme dix et une autre comme huit, elles ne parlent plus de la même adresse. Rejeter les zéros en tête évite toute la question.

05Le motif complet

Mettez le groupe de l'octet à la place de \d{1,3} :

Correspondances10/10 comme prévu
/^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/

Doit correspondre4/4

  • 0.0.0.0
  • 10.0.0.1
  • 192.168.1.1
  • 255.255.255.0

Ne doit pas6/6

  • 256.1.1.1
  • 192.168.1.300
  • 999.999.999.999
  • 010.0.0.1
  • 1.2.3
  • 1.2.3.4.5
Essayez-le dans le bac à sable
Trois groupes « octet puis point », puis un dernier octet. Chaque octet doit aller de 0 à 255, sans zéro en tête.

Il est long, mais parce que l'octet est écrit deux fois, pas parce qu'il est compliqué. Voici ce que {3} fait gagner par rapport à l'octet écrit quatre fois :

Comparaison46 % plus court

Plateau0.0.0.0 · 10.0.0.1 · 192.168.1.1 · 255.255.255.0 · 8.8.4.4 vs 256.1.1.1 · 10.0.0 · 1.2.3.4.5 · 192.168.1.300 · 010.0.0.1

  1. en toutes lettresrésout140
    /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/
  2. avec un grouperésout75
    /^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/
Essayer le plus court
Les deux versions résolvent le plateau. Le groupe avec {3} fait 75 caractères, la version en toutes lettres 140.

06Trouver des adresses dans du texte

^ et $ ne fonctionnent que si toute la saisie est une adresse. Pour trouver des adresses dans une ligne de log, le réflexe habituel est de les remplacer par des limites de mot, \b. Ça marche presque :

Correspondances3/3 comme prévu
/\b((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\b/

Doit correspondre2/2

  • server at 10.0.0.1.
  • version 1.2.3.4.5

Ne doit pas1/1

  • error 999.1.1.1
Essayez-le dans le bac à sable
La deuxième ligne est un numéro de version, pas une adresse, et elle correspond quand même. Un point n'est pas un caractère de mot : il y a donc une limite entre le 4 et le .5.

Ce que vous voulez vraiment, c'est « ni chiffre ni point juste avant, et plus rien qui prolonge l'adresse juste après ». C'est un travail pour les lookarounds :

Correspondances5/5 comme prévu
/(?<![\d.])((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?!\.?\d)/

Doit correspondre2/2

  • server at 10.0.0.1.
  • ping 8.8.8.8 now

Ne doit pas3/3

  • version 1.2.3.4.5
  • error 999.1.1.1
  • build 1.2.3
Essayez-le dans le bac à sable
(?<![\d.]) indique qu'aucun chiffre ni point ne précède. (?!\.?\d) indique qu'aucun chiffre, ni point suivi d'un chiffre, ne suit. Un point final en fin de phrase reste accepté.

Celui-ci fonctionne aussi avec le re de Python. Python n'accepte que les lookbehinds de largeur fixe, et (?<![\d.]) vérifie toujours exactement un caractère.

07Ou utilisez un analyseur

Si vous validez une saisie dans du code, la bibliothèque standard le fait déjà. En Python :

import ipaddress

ipaddress.ip_address("192.168.1.1")             # IPv4Address('192.168.1.1')
ipaddress.ip_address("192.168.1.1").is_private  # True
ipaddress.ip_address("256.1.1.1")               # ValueError

Depuis Python 3.9.5, ipaddress rejette aussi les zéros en tête comme 010.0.0.1. Dans Node, net.isIPv4("010.0.0.1") vaut aussi false, et donne les mêmes réponses que le motif complet ci-dessus sur tous les exemples de cet article. Un analyseur gère aussi l'IPv6, qui demanderait une regex bien plus grosse.

08À vous de jouer

Le cœur de tout le motif, c'est l'octet. Saurez-vous l'écrire de mémoire ?

Chasse jouablepar 35

Attrapez les nombres de 0 à 255, épargnez tout le reste.

Cibles · 0/10 attrapées

  • 0cible
  • 7cible
  • 42cible
  • 99cible
  • 100cible
  • 199cible
  • 200cible
  • 249cible
  • 250cible
  • 255cible

Innocents · erreurs : 0

  • 256innocent
  • 260innocent
  • 300innocent
  • 999innocent
  • 1000innocent
  • 07innocent
  • 007innocent

Tapez une regex — le plateau se met à jour en direct.

Essayez-le dans le bac à sable
Ça vous plaît ? Une nouvelle chasse chaque jour.Jouer la chasse du jour

09Récapitulatif

  • \d{1,3} accepte de 0 à 999. Utilisez (25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d) pour un octet.
  • Échappez chaque point : \..
  • Répétez avec un groupe : (octet\.){3}octet.
  • Utilisez [1-9]?\d pour les petits nombres afin de rejeter les zéros en tête.
  • Dans du texte, utilisez (?<![\d.]) et (?!\.?\d) plutôt que \b.
  • Dans du code, préférez ipaddress (Python) ou net.isIPv4 (Node).

10Continuez à vous entraîner