Przejdź do treści

Walidacja danych: e-mail, PESEL i NIP

  • Teksty i String
  • poziom: średni
  • aktualizacja: 15 sierpnia 2026

Formularz, który przyjmuje byle co, prędzej czy później zapcha Ci bazę śmieciami. Ten walidator sprawdza trzy dane spotykane w każdej polskiej aplikacji: e-mail, PESEL i NIP. Największa pułapka dotyczy PESEL-u — samo policzenie cyfry kontrolnej nie wystarcza, bo numer koduje też datę urodzenia, a miesiąc jest przesunięty o 20 dla roczników 2000+. Dlatego metoda czyPoprawnyPesel najpierw odtwarza datę przez LocalDate.of i odrzuca numery z nieistniejącym dniem.

JAVA Walidator.java 130 linii Pobierz .java
import java.time.DateTimeException;
import java.time.LocalDate;
import java.util.regex.Pattern;

/**
 * Walidacja polskich danych wejściowych: e-mail, PESEL, NIP — skryptyJAVA.pl
 * Zapisz jako: Walidator.java
 */
public class Walidator {

    private static final int[] WAGI_PESEL = {1, 3, 7, 9, 1, 3, 7, 9, 1, 3};
    private static final int[] WAGI_NIP = {6, 5, 7, 2, 3, 4, 5, 6, 7};

    // Praktyczny wzorzec e-maila: część lokalna, małpa, domena z kropką i literowym TLD
    private static final String ZNAK_LOKALNY = "[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]";
    private static final String ETYKIETA = "[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?";
    private static final Pattern WZORZEC_EMAIL = Pattern.compile("^" + ZNAK_LOKALNY + "+(?:\\."
            + ZNAK_LOKALNY + "+)*@" + ETYKIETA + "(?:\\." + ETYKIETA + ")*\\.[A-Za-z]{2,}$");

    /**
     * Sprawdza, czy adres e-mail (może być null) ma sensowną postać: składnię, długości i domenę z kropką.
     * Nie potwierdza, że skrzynka istnieje — to da tylko wysłanie wiadomości z linkiem aktywacyjnym.
     */
    public static boolean czyPoprawnyEmail(String email) {
        if (email == null) { return false; }
        String adres = email.trim();
        int malpa = adres.lastIndexOf('@');
        // część lokalna: min. 1 znak, maks. 64; cały adres maks. 254 znaki
        if (adres.length() > 254 || malpa < 1 || malpa > 64) { return false; }
        return WZORZEC_EMAIL.matcher(adres).matches();
    }

    /**
     * Pełna walidacja numeru PESEL: 11 cyfr, istniejąca data urodzenia i zgodna cyfra kontrolna.
     * W numerze dozwolone są spacje i myślniki.
     */
    public static boolean czyPoprawnyPesel(String pesel) {
        String cyfry = tylkoCyfry(pesel);
        if (cyfry == null || cyfry.length() != 11 || dataUrodzeniaZPesel(cyfry) == null) { return false; }
        int suma = 0;
        for (int i = 0; i < 10; i++) {
            suma += WAGI_PESEL[i] * (cyfry.charAt(i) - '0');
        }
        return (10 - suma % 10) % 10 == cyfry.charAt(10) - '0';
    }

    /**
     * Odczytuje datę urodzenia zakodowaną w numerze PESEL. Stulecie wynika z miesiąca:
     * +0 = lata 1900-1999, +20 = 2000-2099, +40 = 2100-2199, +60 = 2200-2299, +80 = 1800-1899.
     *
     * @return data urodzenia albo null, gdy numer ma złą długość lub data nie istnieje
     */
    public static LocalDate dataUrodzeniaZPesel(String pesel) {
        String cyfry = tylkoCyfry(pesel);
        if (cyfry == null || cyfry.length() != 11) { return null; }
        int dwieCyfryRoku = Integer.parseInt(cyfry.substring(0, 2));
        int zakodowanyMiesiac = Integer.parseInt(cyfry.substring(2, 4));
        int dzien = Integer.parseInt(cyfry.substring(4, 6));
        int rok = switch (zakodowanyMiesiac / 20) {
            case 0 -> 1900 + dwieCyfryRoku;
            case 1 -> 2000 + dwieCyfryRoku;
            case 2 -> 2100 + dwieCyfryRoku;
            case 3 -> 2200 + dwieCyfryRoku;
            default -> 1800 + dwieCyfryRoku;
        };
        try {
            return LocalDate.of(rok, zakodowanyMiesiac % 20, dzien);
        } catch (DateTimeException wyjatek) {
            return null;   // np. 30 lutego albo miesiąc 00 czy 13
        }
    }

    /**
     * Zwraca płeć zakodowaną w numerze PESEL — decyduje 10. cyfra (parzysta = kobieta).
     * Wynik to "kobieta" albo "mezczyzna", a null, gdy numer jest niepoprawny.
     */
    public static String plecZPesel(String pesel) {
        if (!czyPoprawnyPesel(pesel)) { return null; }
        return (tylkoCyfry(pesel).charAt(9) - '0') % 2 == 0 ? "kobieta" : "mezczyzna";
    }

    /**
     * Walidacja numeru NIP: 10 cyfr i suma kontrolna z wagami 6,5,7,2,3,4,5,6,7 modulo 11.
     * W numerze dozwolone są spacje i myślniki, np. "525-244-57-67".
     */
    public static boolean czyPoprawnyNip(String nip) {
        String cyfry = tylkoCyfry(nip);
        if (cyfry == null || cyfry.length() != 10) { return false; }
        int suma = 0;
        for (int i = 0; i < 9; i++) {
            suma += WAGI_NIP[i] * (cyfry.charAt(i) - '0');
        }
        int kontrolna = suma % 11;
        // reszty 10 nie da się zapisać jedną cyfrą — taki NIP po prostu nie istnieje
        return kontrolna != 10 && kontrolna == cyfry.charAt(9) - '0';
    }

    /** Zostawia same cyfry; zwraca null, gdy tekst zawiera coś poza cyframi, spacjami i myślnikami. */
    private static String tylkoCyfry(String tekst) {
        if (tekst == null) { return null; }
        StringBuilder wynik = new StringBuilder(tekst.length());
        for (char znak : tekst.toCharArray()) {
            if (znak >= '0' && znak <= '9') {
                wynik.append(znak);
            } else if (znak != '-' && znak != ' ') { return null; }
        }
        return wynik.toString();
    }

    // ── PRZYKŁADY ───────────────────────────────────────────
    public static void main(String[] args) {
        System.out.println("E-MAIL");
        for (String email : new String[]{"anna.kowalska@firma.com.pl", "jan+sklep@example.org",
                "bez.malpy.example.com", "dwie..kropki@example.com", "jan@localhost"}) {
            System.out.printf("  %-26s -> %s%n", email, czyPoprawnyEmail(email));
        }

        System.out.println("PESEL");
        for (String pesel : new String[]{"90051212349", "04320781539", "90051212340", "90023012345"}) {
            boolean poprawny = czyPoprawnyPesel(pesel);
            System.out.printf("  %-13s -> %-5s %s%n", pesel, poprawny,
                    poprawny ? dataUrodzeniaZPesel(pesel) + ", " + plecZPesel(pesel) : "odrzucony");
        }

        System.out.println("NIP");
        for (String nip : new String[]{"1234563218", "525-244-57-67", "123 456 32 18", "1234567890"}) {
            System.out.printf("  %-15s -> %s%n", nip, czyPoprawnyNip(nip));
        }
    }
}

Jak użyć tego skryptu

  1. Skopiuj kod i zapisz go w pliku o nazwie Walidator.java — nazwa pliku musi być taka sama jak nazwa klasy publicznej.
  2. Otwórz terminal w katalogu z plikiem i wpisz java Walidator.java. Zobaczysz trzy sekcje przykładów: e-mail, PESEL i NIP, z wynikiem dla danych poprawnych i błędnych.
  3. We własnym projekcie wywołuj metody statycznie: Walidator.czyPoprawnyPesel(pole), Walidator.czyPoprawnyNip(pole), Walidator.czyPoprawnyEmail(pole). Wszystkie przyjmują null i zwracają wtedy false.
  4. Metody dataUrodzeniaZPesel i plecZPesel wyciągną z numeru dodatkowe dane. Uważaj na różnicę: plecZPesel odrzuca każdy błędny numer, a dataUrodzeniaZPesel cyfry kontrolnej nie liczy — dla 90051212340 odda 1990-05-12. Datę odczytuj dopiero po czyPoprawnyPesel.

Warto wiedzieć

  • Poprawny format to jeszcze nie istniejący adresat. Że skrzynka działa, potwierdzi Ci dopiero wiadomość z linkiem aktywacyjnym, a PESEL i NIP z dobrą sumą kontrolną wygenerujesz w kilka sekund — 1111111111 to poprawny NIP, jak każdy ciąg dziesięciu jednakowych cyfr. Kontrahenta sprawdzisz w wykazie podatników VAT Ministerstwa Finansów.
  • Wzorzec e-maila ma odsiać literówki, a nie realizować normy RFC 5322 — regexy „zgodne z RFC" mają setki znaków i tak przepuszczają adresy, których żaden serwer nie obsłuży. Stąd świadomy skutek uboczny: adres bez kropki w domenie dostaje false, dlatego w przykładach odpada jan@localhost. Technicznie poprawny, ale w formularzu rejestracji to prawie zawsze literówka.
  • PESEL i data urodzenia to dane osobowe w rozumieniu RODO. Nie loguj ich do plików, nie wysyłaj w adresie URL i zbieraj tylko wtedy, gdy naprawdę są potrzebne.
  • Metoda tylkoCyfry celowo zwraca null przy znaku innym niż cyfra, spacja lub myślnik — dzięki temu „123abc4567" jest odrzucone, a nie po cichu okrojone do „1234567". Jeśli chcesz akceptować też kropki albo prefiks PL przy NIP-ie, dopisz je do warunku.
#walidacja #regex #parsowanie #java.time

Podobne skrypty