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.