RegExp wyszukiwanie znaków do wyrazu
.*?(?=WYRAZ)
gdzie:
• .*? – leniwie (najkrócej) dopasowuje dowolne znaki,
• (?=WYRAZ) – pozytywne spojrzenie w przód; upewnia się, że tuż za dopasowaniem występuje słowo WYRAZ, ale nie włącza go do wyniku.
Kluczowe punkty
• Użyj kwantyfikatora leniwego (*? lub +?), aby zatrzymać się na pierwszym pasującym wyrazie.
• Spojrzenia (look-around) pozwalają wykluczyć słowo graniczne z dopasowania.
• W środowisku Delphi można skorzystać z klas System.RegularExpressions (XE2+) lub TRegExpr.
| Cel | Wzorzec z grupą | Wzorzec ze spojrzeniem (zalecany) |
|---|---|---|
Wszystko przed słowem END |
(.*?)END |
.*?(?=END) |
Wszystko pomiędzy START i END |
START(.*?)END |
(?<=START)(.*?)(?=END) |
Wszystko po słowie START (bez niego) do końca linii |
START(.*) |
(?<=START).* |
• Wersja ze spojrzeniami jest krótsza i zwraca dokładnie wymagany fragment – nie trzeba później usuwać słowa granicznego.
. – odpowiada jednemu dowolnemu znakowi (bez \n, chyba że użyto flagi s). * (0 +), + (1 +); dodanie ? czyni je leniwszymi (dopasowanie minimalne). (?=...) i wstecz (?<=...) – asercje zerowej szerokości, nie „konsumują” znaków. ^, $ – początek/koniec linii lub tekstu (w zależności od trybu). uses System.RegularExpressions;
function GetTextUpToWord(const Src, StopWord: string): string;
var
Pattern: string;
M: TMatch;
begin
// \Q ... \E – pozwala automatycznie „uciec” znaki specjalne w StopWord
Pattern := '.*?(?=\Q' + StopWord + '\E)';
M := TRegEx.Match(Src, Pattern, [roSingleLine]); // roSingleLine = DOTALL
if M.Success then
Result := M.Value
else
Result := '';
end;
W starszych IDE można użyć TRegExpr (RegExpr.pas).
• Obecne silniki RegExp (PCRE2, .NET, JavaScript ES2022, Delphi 12) obsługują look-behind o zmiennej długości i flagę s (dotall), co upraszcza wzorce wieloliniowe.
• Coraz częściej stosuje się bibliotekę TPerlRegEx (PCRE) lub wbudowane TRegex z PCRE2 w Delphi 12, dzięki czemu składnia jest zgodna z PCRE/JS.
• Narzędzia do testowania (regex101.com, regexper.com) umożliwiają wizualizację i profilowanie wydajności.
\w w PCRE2 w trybie UTF-8 obejmuje je automatycznie (re::uc). W starszych silnikach definiuj klasę znaków jawnie: [\p{L}\p{N}_] // litery wszystkich alfabetów + cyfry i _
\n, użyj flagi s (dotall) lub zamień . na [\s\S]. \G) lub biblioteki streamingowe (RE2, Hyperscan). • Logi i zrzuty mogą zawierać dane osobowe – upewnij się, że wyrażenia regularne nie naruszają RODO (maskowanie danych wrażliwych).
• W automatycznych parserach trzeba ograniczyć możliwość wstrzyknięcia złośliwych wzorców (np. z danych użytkownika).
\Q..\E lub TRegEx.Escape) wprowadzane przez użytkownika słowo graniczne. i lub dodaj (?i) w razie potrzeby. .*? przed alternatywą typu |, bo może prowadzić do katastrofalnego back-trackingu. • Nie każdy silnik obsługuje (?<=...) o zmiennej długości (np. JavaScript < ES2018).
• Wzorce „greedy” zamiast „lazy” potrafią pochłonąć gigabajty tekstu – profiluj!
• . bez flagi s zatrzymuje się na \n, co może fałszywie skrócić dopasowanie.
• Analiza wydajności RegExp vs. parsery strumieniowe (RE2, Hyperscan).
• Zautomatyzowane generowanie wzorców z użyciem LLM (np. GitHub Copilot).
• Idiomy PCRE „atomic grouping” ( (?>...) ) i „possessive quantifiers” (*+, ++) do eliminacji back-trackingu.
Wyrażenie
.*?(?=WYRAZ)
jest najprostszą, uniwersalną i wydajną metodą wyciągnięcia wszystkich znaków do zadanego wyrazu bez wciągania samego wyrazu. W Delphi (i większości współczesnych języków) realizuje się je przez TRegEx lub TRegExpr, pamiętając o fladze s dla tekstu wieloliniowego i o escapowaniu znaków specjalnych. Odpowiednie stosowanie spojrzeń, kwantyfikatorów leniwych i testów brzegowych gwarantuje stabilny oraz dokładny parser.
RegExpr, jak wyszykiwać wyrazy 'ala' tylko do wyrazu 'implementation'
\bala\b(?=.*?\bimplementation\b)
^.*?(?=\bimplementation\b) // flaga s (dotAll) włączona
\bala\b
Kluczowe punkty
• \b – granica słowa, dzięki czemu „ala” nie złapie się w „kalafior”.
• Look-ahead (?= … ) sprawdza tylko warunek – sam „implementation” nie jest częścią dopasowania.
• Flaga s (dotAll) lub reg.ModifierS := True w TRegExpr jest konieczna przy tekście wieloliniowym.
.*? jest leniwa – zatrzymuje się na pierwszym możliwym miejscu, co minimalizuje back-tracking. uses
RegExpr; // biblioteka TRegExpr (Lazarus, FPC)
// lub System.RegularExpressions; // Delphi XE & nowsze
function FindAlaBeforeImpl(const S: string): TStringList;
var
R: TRegExpr;
begin
Result := TStringList.Create;
R := TRegExpr.Create;
try
R.Expression := '\bala\b(?=.*?\bimplementation\b)';
R.ModifierS := True; // kropka obejmuje znaki nowej linii
R.ModifierI := True; // opcjonalnie: ignoruj wielkość liter
if R.Exec(S) then
repeat
Result.Add(Format('Pos=%d -> %s', [R.MatchPos[0], R.Match[0]]));
until not R.ExecNext;
finally
R.Free;
end;
end;
Wariant dwu-etapowy z System.RegularExpressions (Delphi):
function ListAlaBeforeFirstImpl(const S: string): TArray<Integer>;
var
Head : string;
Col : TMatchCollection;
M : TMatch;
Offsets: TList<Integer>;
begin
// 1. Wytnij tekst do pierwszego "implementation"
Head := TRegEx.Match(S, '^.*?(?=\bimplementation\b)',
[roSingleLine, roIgnoreCase]).Value;
// 2. Szukaj "ala" w wyciętym fragmencie
Col := TRegEx.Matches(Head, '\bala\b', [roIgnoreCase]);
Offsets := TList<Integer>.Create;
for M in Col do Offsets.Add(M.Index);
Result := Offsets.ToArray;
Offsets.Free;
end;
.*? + look-ahead back-trackuje tylko do pierwszego „implementation”, co w praktyce jest akceptowalne nawet dla plików setek kB. i/ModifierI – jeżeli wielkość liter ma być ignorowana (Ala, ALA, …). implementation, można zastąpić .*? konstrukcją do pierwszej kotwicy \A[^]*?\bimplementation\b. (?:(?!implementation).)*?) pozwala wyszukiwać kolejne „ala”, ale jest wolniejszy; warto go unikać jeśli nie jest konieczny. ModifierS := True (RegExpr) albo roSingleLine (TRegEx), inaczej . nie obejmie znaków CR/LF. 'implementation' w literale string, brak „implementation” w pliku. \b zgodnie z Unicode – mogą źle traktować polskie znaki. Upewnij się, że korzystasz z wersji z włączoną obsługą UTF-8. interface/implementation bez regex-ów. W praktyce wystarczają dwa sprawdzone scenariusze:
Jedno-etapowy, z look-ahead:
\bala\b(?=.*?\bimplementation\b)
Szybki, prosty, dobry dla większości plików.
Dwu-etapowy (odcięcie do pierwszego „implementation” + proste wyszukiwanie):
• stabilniejszy przy plikach wielomegabajtowych, łatwiejszy do debugowania.
Oba działają w TRegExpr, System.RegularExpressions oraz dowolnym silniku zgodnym z PCRE. Wybór zależy od rozmiaru danych i wymagań dotyczących niezawodności.