W centrum kontroli Parku Jurajskiego, Dennis Nedry właśnie otrzymał zadanie: przeszukać tysiące logów systemowych w poszukiwaniu anomalii. "Nie mam zamiaru przeglądać tego ręcznie," mruczy pod nosem. "Użyję wyrażeń regularnych - najpotężniejszego narzędzia do wyszukiwania wzorców w tekście!"
Wyrażenia regularne (Regular Expressions, w skrócie RegEx lub RegExp) to wzorce służące do dopasowywania ciągów znaków w tekście. Są jak "DNA wyszukiwania" - definiują dokładnie, czego szukamy.
1// Najprostsze wyrażenie regularne - szukamy dokładnego tekstu
2const simplePattern = /dinozaur/;
3const text = "W parku jest dinozaur T-Rex.";
4
5console.log(simplePattern.test(text)); // true - znaleziono "dinozaur"Są dwa sposoby tworzenia RegEx w JavaScript:
1// 1. Literał (zalecany dla stałych wzorców)
2const pattern1 = /T-Rex/;
3
4// 2. Konstruktor (dla dynamicznych wzorców)
5const species = "Velociraptor";
6const pattern2 = new RegExp(species);
7
8// Konstruktor z flagami
9const pattern3 = new RegExp("raptor", "gi"); // g = global, i = ignore case1const dinoPattern = /Rex/;
2
3console.log(dinoPattern.test("T-Rex jest groźny")); // true
4console.log(dinoPattern.test("Velociraptor ucieka")); // false1const log = "ALERT: T-Rex w sektorze 7. ALERT: Raptor w sektorze 3.";
2
3// Bez flagi 'g' - pierwsze dopasowanie
4console.log(log.match(/ALERT/));
5// ["ALERT", index: 0, input: "...", groups: undefined]
6
7// Z flagą 'g' - wszystkie dopasowania
8console.log(log.match(/ALERT/g));
9// ["ALERT", "ALERT"]1const status = "Status: DANGER - T-Rex escaped";
2
3// Zamiana tekstu
4const safeStatus = status.replace(/DANGER/, "SAFE");
5console.log(safeStatus); // "Status: SAFE - T-Rex escaped"
6
7// Zamiana wszystkich wystąpień
8const log = "Error 404. Error 500. Error 403.";
9const fixedLog = log.replace(/Error/g, "Warning");
10console.log(fixedLog); // "Warning 404. Warning 500. Warning 403."1const dinoList = "T-Rex, Velociraptor, Triceratops, Brachiosaurus";
2
3// Podział po przecinku i spacji
4const dinos = dinoList.split(/,\s*/);
5console.log(dinos);
6// ["T-Rex", "Velociraptor", "Triceratops", "Brachiosaurus"]1// \d - dowolna cyfra (0-9)
2const sectorLog = "Sektor 7 - status OK";
3console.log(sectorLog.match(/\d/g)); // ["7"]
4
5// \w - znak słowny (litera, cyfra, podkreślenie)
6const code = "DINO_001_REX";
7console.log(code.match(/\w+/g)); // ["DINO_001_REX"]
8
9// \s - biały znak (spacja, tab, nowa linia)
10const text = "Park\tJurajski\nStatus";
11console.log(text.split(/\s+/)); // ["Park", "Jurajski", "Status"]
12
13// . (kropka) - dowolny znak (oprócz nowej linii)
14const pattern = /T.Rex/;
15console.log(pattern.test("T-Rex")); // true
16console.log(pattern.test("T Rex")); // true
17console.log(pattern.test("TXRex")); // true1// \D - wszystko oprócz cyfr
2// \W - wszystko oprócz znaków słownych
3// \S - wszystko oprócz białych znaków
4
5const mixed = "Dino123!@#";
6console.log(mixed.match(/\D/g)); // ["D", "i", "n", "o", "!", "@", "#"]1// [abc] - jeden z wymienionych znaków
2const vowels = /[aeiou]/gi;
3console.log("Tyrannosaurus".match(vowels)); // ["a", "o", "a", "u"]
4
5// [a-z] - zakres znaków
6const lowercase = /[a-z]+/g;
7console.log("T-Rex123".match(lowercase)); // ["ex"]
8
9// [^abc] - negacja - wszystko oprócz wymienionych
10const notDigit = /[^0-9]+/g;
11console.log("Sektor7A".match(notDigit)); // ["Sektor", "A"]
12
13// Łączenie zakresów
14const alphanumeric = /[a-zA-Z0-9]+/g;
15console.log("ID: DINO-001".match(alphanumeric)); // ["ID", "DINO", "001"]1// * - zero lub więcej
2const pattern1 = /ro*ar/;
3console.log(pattern1.test("rar")); // true (0 razy 'o')
4console.log(pattern1.test("roar")); // true (1 raz 'o')
5console.log(pattern1.test("roooar")); // true (wiele razy 'o')
6
7// + - jeden lub więcej
8const pattern2 = /ro+ar/;
9console.log(pattern2.test("rar")); // false (potrzeba min. 1 'o')
10console.log(pattern2.test("roar")); // true
11
12// ? - zero lub jeden
13const pattern3 = /colou?r/;
14console.log(pattern3.test("color")); // true (amerykański)
15console.log(pattern3.test("colour")); // true (brytyjski)
16
17// {n} - dokładnie n razy
18const idPattern = /DINO-\d{3}/;
19console.log(idPattern.test("DINO-001")); // true
20console.log(idPattern.test("DINO-01")); // false
21
22// {n,m} - od n do m razy
23const codePattern = /[A-Z]{2,4}-\d{3,5}/;
24console.log(codePattern.test("REX-001")); // true
25console.log(codePattern.test("TREX-12345")); // true
26console.log(codePattern.test("A-1")); // false1// ^ - początek tekstu/linii
2const startPattern = /^ALERT/;
3console.log(startPattern.test("ALERT: Dinozaur uciekł")); // true
4console.log(startPattern.test("Status: ALERT")); // false
5
6// $ - koniec tekstu/linii
7const endPattern = /OK$/;
8console.log(endPattern.test("Status: OK")); // true
9console.log(endPattern.test("OK - done")); // false
10
11// \b - granica słowa
12const wordBoundary = /\bRex\b/;
13console.log(wordBoundary.test("T-Rex")); // true
14console.log(wordBoundary.test("Rexona")); // false (Rex nie jest osobnym słowem)1// () - grupa przechwytująca
2const datePattern = /(\d{4})-(\d{2})-(\d{2})/;
3const date = "2024-06-15";
4const match = date.match(datePattern);
5console.log(match[0]); // "2024-06-15" (całe dopasowanie)
6console.log(match[1]); // "2024" (pierwsza grupa)
7console.log(match[2]); // "06" (druga grupa)
8console.log(match[3]); // "15" (trzecia grupa)
9
10// | - alternatywa (lub)
11const dinoPattern = /T-Rex|Velociraptor|Triceratops/;
12console.log(dinoPattern.test("T-Rex spotted")); // true
13console.log(dinoPattern.test("Velociraptor alert")); // true
14console.log(dinoPattern.test("Brachiosaurus")); // false
15
16// (?:) - grupa nieprzechwytująca
17const efficientPattern = /(?:Mr|Mrs|Dr)\.?\s+(\w+)/;
18const title = "Dr. Grant";
19const nameMatch = title.match(efficientPattern);
20console.log(nameMatch[1]); // "Grant" (tylko imię, nie tytuł)1// g - global (znajdź wszystkie dopasowania)
2console.log("Rex Rex Rex".match(/Rex/)); // ["Rex"] (tylko pierwsze)
3console.log("Rex Rex Rex".match(/Rex/g)); // ["Rex", "Rex", "Rex"]
4
5// i - ignore case (ignoruj wielkość liter)
6console.log(/trex/i.test("T-REX")); // true
7console.log(/trex/.test("T-REX")); // false
8
9// m - multiline (^ i $ dotyczą każdej linii)
10const multiline = `Sektor 1: OK
11Sektor 2: ALERT
12Sektor 3: OK`;
13
14console.log(multiline.match(/^Sektor/gm));
15// ["Sektor", "Sektor", "Sektor"] (początek każdej linii)
16
17// s - dotAll (. dopasowuje też nową linię)
18const withNewline = "Start\nEnd";
19console.log(/Start.End/.test(withNewline)); // false
20console.log(/Start.End/s.test(withNewline)); // true1function validateDinoId(id) {
2 // Format: 3 litery - 3 cyfry - 2 litery
3 // Przykład: REX-001-JP, TRI-042-JP
4 const pattern = /^[A-Z]{3}-\d{3}-[A-Z]{2}$/;
5 return pattern.test(id);
6}
7
8console.log(validateDinoId("REX-001-JP")); // true
9console.log(validateDinoId("TREX-01-J")); // false (nieprawidłowy format)
10console.log(validateDinoId("rex-001-jp")); // false (małe litery)1function parseSecurityLog(log) {
2 // Format: [TIMESTAMP] LEVEL: Message - Sector X
3 const pattern = /\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (\w+): (.+) - Sector (\d+)/;
4
5 const match = log.match(pattern);
6 if (!match) return null;
7
8 return {
9 timestamp: match[1],
10 level: match[2],
11 message: match[3],
12 sector: parseInt(match[4])
13 };
14}
15
16const log = "[2024-06-15 14:30:22] ALERT: Fence breach detected - Sector 7";
17console.log(parseSecurityLog(log));
18// {
19// timestamp: "2024-06-15 14:30:22",
20// level: "ALERT",
21// message: "Fence breach detected",
22// sector: 7
23// }1function validateEmail(email) {
2 // Prosty wzorzec emaila
3 const pattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
4 return pattern.test(email);
5}
6
7console.log(validateEmail("alan.grant@jurassicpark.com")); // true
8console.log(validateEmail("invalid-email")); // false
9console.log(validateEmail("test@.com")); // false1function extractCoordinates(text) {
2 // Format: szerokość, długość (np. 18.4655, -66.1057)
3 const pattern = /-?\d+\.\d+,\s*-?\d+\.\d+/g;
4
5 const matches = text.match(pattern);
6 if (!matches) return [];
7
8 return matches.map(coord => {
9 const [lat, lng] = coord.split(/,\s*/);
10 return { latitude: parseFloat(lat), longitude: parseFloat(lng) };
11 });
12}
13
14const report = "T-Rex spotted at 18.4655, -66.1057. Raptor at 18.4701, -66.1123.";
15console.log(extractCoordinates(report));
16// [
17// { latitude: 18.4655, longitude: -66.1057 },
18// { latitude: 18.4701, longitude: -66.1123 }
19// ]1function sanitizeInput(input) {
2 // Usuń potencjalnie niebezpieczne znaki
3 return input
4 .replace(/<[^>]*>/g, '') // Usuń tagi HTML
5 .replace(/[<>\"'&]/g, '') // Usuń niebezpieczne znaki
6 .replace(/\s+/g, ' ') // Zamień wielokrotne spacje na jedną
7 .trim();
8}
9
10const userInput = "<script>alert('hack')</script> Dinosaur report ";
11console.log(sanitizeInput(userInput));
12// "scriptalerthackscript Dinosaur report"1// ✅ Kompiluj wzorzec raz, używaj wielokrotnie
2const dinoIdPattern = /^[A-Z]{3}-\d{3}$/;
3
4function validateManyIds(ids) {
5 return ids.filter(id => dinoIdPattern.test(id));
6}
7
8// ❌ Nie twórz wzorca w pętli
9function badValidation(ids) {
10 return ids.filter(id => /^[A-Z]{3}-\d{3}$/.test(id)); // Nowy RegExp w każdej iteracji!
11}
12
13// ✅ Używaj grup nieprzechwytujących gdy nie potrzebujesz grup
14const efficient = /(?:Mr|Mrs|Dr)\.\s+\w+/; // (?:...) nie tworzy grupy przechwytującej
15
16// ✅ Unikaj nadmiernego backtrackingu
17// ❌ Źle: /.*foo/ (może być bardzo wolne)
18// ✅ Dobrze: /[^f]*foo/ lub użyj lazy quantifier /.*?foo/Dennis Nedry kończy prezentację: "Wyrażenia regularne to jak system DNA w naszym oprogramowaniu - pozwalają precyzyjnie identyfikować i manipulować wzorcami tekstowymi. Pamiętajcie:"
/pattern/ dla stałych wzorcówincludes() lub indexOf() wystarczy