
Aby sprawdzić format adresu e-mail, przetestuj go poniższym wyrażeniem regularnym (test() w JavaScripcie, preg_match() w PHP). Przyjmuje prenom.nom+tag@sous.domaine.fr, a odrzuca podwojone kropki, spacje i domeny bez rozszerzenia. Nie gwarantuje, że skrzynka istnieje: do tego trzeba wysłać wiadomość albo odpytać domenę.
Regex email robi jedną rzecz: sprawdza, czy ciąg znaków ma postać adresu poczty elektronicznej. To niewiele, a jest niezbędne w każdym formularzu rejestracji, kontaktu czy zamówienia. Oto wyrażenie regularne, którego używam od lat, w wersji dla JavaScriptu, PHP i HTML-a, a przede wszystkim szczegółowy opis tego, co przyjmuje, a co odrzuca.
Regex email gotowy do skopiowania
^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$Czytany od lewej do prawej:
^i$: cały ciąg musi być adresem, a nie tylko go zawierać.[^<>()[\]\\.,;:\s@"]+: część lokalna (przed@) składa się z dozwolonych znaków, bez spacji i zastrzeżonej interpunkcji. Grupy rozdzielone kropką (prenom.nom) są przyjmowane, podwojona kropka już nie.|(".+"): alternatywa w cudzysłowie, przewidziana w normie, dopuszcza"jean dupont"@example.com.@, a po nim domena: albo adres IP w nawiasach kwadratowych ([192.168.1.1]), albo etykiety alfanumeryczne rozdzielone kropkami i zakończone rozszerzeniem o długości co najmniej dwóch liter.
Ta reguła zastąpiła w październiku 2022 roku wersję, w której podczas migracji serwisu w 2021 roku zgubiły się odwrotne ukośniki. Jeśli skopiowałeś tamtą, wymień ją: przepuszczała źle sformowane adresy.
Co regex sprawdza, a czego nie sprawdza
Poniższa tabela to rzeczywisty wynik test() w Node.js 22 i preg_match() w PHP 8.5 dla szesnastu adresów. Oba silniki wydają ten sam werdykt.
| Testowany adres | Wynik | Dlaczego |
|---|---|---|
jean.dupont@example.com | przyjęty | przypadek standardowy |
jean+news@sub.example.co.uk | przyjęty | znak + i subdomeny są poprawne |
JEAN@EXAMPLE.COM | przyjęty | flaga i ignoruje wielkość liter |
"jean dupont"@example.com | przyjęty | część lokalna w cudzysłowie, przewidziana w RFC 5322 |
jean@[192.168.1.1] | przyjęty | domena w postaci adresu IP |
jéan@exemple.fr | przyjęty | część lokalna dopuszcza znaki spoza ASCII |
jean@localhost | odrzucony | brak rozszerzenia po kropce |
jean@example | odrzucony | ten sam powód |
jean..dupont@example.com | odrzucony | podwojona kropka w części lokalnej |
jean@exa_mple.com | odrzucony | podkreślenie nie jest dozwolone w nazwie domeny |
jean@exemple.café | odrzucony | rozszerzenie ze znakiem diakrytycznym: zobacz ograniczenia |
a@b.c | odrzucony | rozszerzenie z jednej litery |
@example.com | odrzucony | pusta część lokalna |
jean@.com | odrzucony | pusta domena przed rozszerzeniem |
jean dupont@example.com | odrzucony | spacja |
jean@example.com (spacja na końcu) | odrzucony | kotwica $ odrzuca każdy znak po rozszerzeniu: pamiętaj o trim() |
Czego żaden regex nie sprawdzi: że domena istnieje, że przyjmuje pocztę i że skrzynka działa. personne@gmail.com ma nienaganną postać i prawdopodobnie nic nie odbiera.
Wersja JavaScript
Funkcja przyjmuje adres jako parametr, usuwa otaczające spacje i zwraca true albo false. Flaga i sprawia, że porównanie nie rozróżnia wielkości liter.
/**
* Sprawdza format adresu e-mail.
* @param {string} email
* @returns {boolean}
*/
function validateEmail(email) {
const emailReg = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i;
return emailReg.test(String(email).trim());
}
console.log(validateEmail('jean.dupont@example.com')); // true
console.log(validateEmail('jean..dupont@example.com')); // false
console.log(validateEmail(' jean@example.com ')); // true, dzięki trimW formularzu
Typowe zastosowanie: zablokować wysyłkę i pokazać komunikat, dopóki adres jest źle sformowany.
const form = document.querySelector('#inscription');
const champ = document.querySelector('#email');
const erreur = document.querySelector('#email-erreur');
form.addEventListener('submit', (event) => {
if (!validateEmail(champ.value)) {
event.preventDefault();
erreur.textContent = 'Adresse e-mail invalide';
champ.setAttribute('aria-invalid', 'true');
champ.focus();
}
});Pole błędu to widoczny element obok pola formularza, a nie okno alert(): użytkownik nie traci kontekstu i może od razu poprawić wpis.
Wersja PHP
Walidacja po stronie klienta pomaga przy wypełnianiu, ale nie jest zabezpieczeniem. Serwer musi powtórzyć kontrolę: JavaScript da się wyłączyć, a formularz wywołać bezpośrednio. Ten sam regex działa z preg_match(), z dokładnością do cudzysłowów.
/**
* Sprawdza format adresu e-mail.
*/
function isValidEmail(string $email): bool
{
$pattern = '/^(([^<>()[\]\\\\.,;:\s@"]+(\.[^<>()[\]\\\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i';
return preg_match($pattern, trim($email)) === 1;
}
var_dump(isValidEmail('jean.dupont@example.com')); // bool(true)
var_dump(isValidEmail('jean@localhost')); // bool(false)Zwróć uwagę na podwójne escapowanie odwrotnych ukośników w łańcuchu PHP w apostrofach: \\\\ w kodzie daje \\ we wzorcu, co dla silnika wyrażeń regularnych oznacza „dosłowny odwrotny ukośnik”.
Regex czy filter_var?
PHP udostępnia filter_var($email, FILTER_VALIDATE_EMAIL), które trzyma się normy bliżej. Oba podejścia są poprawne; nie rozstrzygają jednak dokładnie tak samo:
| Adres | Regex z artykułu | filter_var |
|---|---|---|
a@b.c | odrzucony | przyjęty |
"jean dupont"@example.com | przyjęty | odrzucony |
jéan@exemple.fr | przyjęty | odrzucony bez FILTER_FLAG_EMAIL_UNICODE, przyjęty z tą flagą |
jean+news@sub.example.co.uk | przyjęty | przyjęty |
W formularzu publicznym filter_var z flagą Unicode jest dobrym wyborem domyślnym; regex pozostaje przydatny, gdy chcesz mieć dokładnie tę samą regułę w JavaScripcie i w PHP.
Wersja HTML
Przeglądarka potrafi już zweryfikować e-mail: type="email" blokuje wysłanie formularza przy złym formacie i wyświetla własny komunikat. Atrybut pattern pozwala dodać ostrzejszą regułę, na przykład tę z artykułu.
<label for="email">Adresse e-mail</label>
<input
id="email"
name="email"
type="email"
required
autocomplete="email"
pattern="(([^<>\(\)\[\]\\.,;:\s@"]+(\.[^<>\(\)\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))"
title="Une adresse de la forme prenom.nom@domaine.fr"
>Cztery szczegóły: pattern jest niejawnie zakotwiczony, nie należy więc pisać ^ ani $; cudzysłowy we wzorcu zapisuje się jako ", żeby nie zamknąć atrybutu; przeglądarki kompilują pattern z flagą v, która wymaga escapowania nawiasów okrągłych i kwadratowych wewnątrz klasy znaków (\(\)\[\]), inaczej wzorzec jest po cichu ignorowany; a walidację HTML obchodzi się jedną linijką w konsoli. Poprawia komfort użytkownika, ale nie zastępuje kontroli na serwerze.
Przypadki brzegowe, które warto znać
- Znak
+:jean+boutique@gmail.comto poprawny adres, którego wiele osób używa do sortowania poczty. Regex, który go odrzuca, kosztuje rejestracje. - Długie rozszerzenia:
.photography,.paris,.technologyistnieją. Dlatego wzorzec kończy się na[a-zA-Z]{2,}, a nie na zamkniętej liście rozszerzeń. - Znaki diakrytyczne: część lokalna je przyjmuje; domena w tym wyrażeniu już nie (domeny internacjonalizowane zapisuje się w rzeczywistości w Punycode,
xn--…, zanim zostaną rozwiązane). - Otaczające spacje: kopiowanie z arkusza kalkulacyjnego często dokłada spację na końcu. Wywołaj
trim()przed testem, tak jak robią to obie funkcje powyżej. - Wielkość liter: domena jej nie rozróżnia; część lokalna teoretycznie tak, ale żaden popularny dostawca nie robi tej różnicy. Porównywanie małymi literami to oczekiwane zachowanie.
Przetestuj regex przed wdrożeniem
Zestaw testowy jest wart więcej niż ponowne przeczytanie kodu. W JavaScripcie wystarczy pętla po znanych adresach:
const cas = {
'jean.dupont@example.com': true,
'jean+news@sub.example.co.uk': true,
'jean@localhost': false,
'jean..dupont@example.com': false,
'jean dupont@example.com': false,
};
for (const [email, attendu] of Object.entries(cas)) {
const ok = validateEmail(email) === attendu;
console.log(`${ok ? 'OK ' : 'KO '} ${email}`);
}Do analizy wzorca regex101 koloruje każdą grupę i tłumaczy, co przechwytuje. Wybierz odmianę ECMAScript dla JavaScriptu i PCRE2 dla PHP: obie przyjmują ten regex bez zmian.
Krok dalej: sprawdzić, czy adres istnieje
Gdy postać jest poprawna, trzy dodatkowe kontrole ograniczają liczbę fałszywych adresów, od najtańszej do najpewniejszej:
- Odrzucanie domen jednorazowych (adresów tymczasowych zakładanych po to, by obejść rejestrację) na podstawie utrzymywanej listy.
- Odpytanie rekordów MX domeny po stronie serwera: domena bez serwera poczty nigdy nie odbierze wiadomości. W PHP
checkdnsrr($domaine, 'MX')odpowiada w jednej linijce. - Wysłanie e-maila potwierdzającego z linkiem do kliknięcia. To jedyny dowód, że ktoś czyta tę skrzynkę, i tak robią wszystkie poważne serwisy.
Dwie pierwsze kontrole stosuję na publicznych formularzach Foliade; trzecia pozostaje punktem odniesienia wszędzie tam, gdzie zakładane jest konto.
Pozostałe wyrażenia regularne do walidacji
To samo podejście, inne formaty: walidacja daty, adresu IP, kodu pocztowego albo hasła.
Częste błędy
test() przyjmuje „bonjour jean@example.com merci”, bo poprawny adres znajduje się gdzieś w ciągu. Funkcja z tego artykułu kotwiczy wyrażenie na obu końcach.jean+newsletter@…) albo nowe, długie rozszerzenia w rodzaju .photography. Zawsze testuj na realnych adresach, zanim zaostrzysz regułę.

