Czy istnieje sposób na uzyskanie odpowiednika negatywnego lookbehind w wyrażeniach regularnych javascript? Muszę dopasować ciąg, który nie zaczyna się od określonego zestawu znaków.
Wygląda na to, że nie mogę znaleźć wyrażenia regularnego, które robi to bez błędu, jeśli dopasowana część znajduje się na początku ciągu. Negatywne lookbehinds wydaje się jedyną odpowiedzią, ale javascript go nie ma.
EDYCJA: To jest wyrażenie regularne, nad którym chciałbym pracować, ale tak nie jest:
(?<!([abcdefg]))m
Więc pasowałoby do „m” w „jim” lub „m”, ale nie do „jam”
javascript
regex
negative-lookbehind
Andrew Ensley
źródło
źródło
(?:[^abcdefg]|^)(m)
? Jak w"mango".match(/(?:[^abcdefg]|^)(m)/)[1]
Odpowiedzi:
Lookbehind Assertions zostało zaakceptowane w specyfikacji ECMAScript w 2018 roku.
Pozytywne użycie lookbehind:
Użycie negatywnego lookbehind:
Wsparcie platformy:
źródło
Od 2018 roku Lookbehind Assertions są częścią specyfikacji języka ECMAScript .
Odpowiedź przed 2018 rokiem
Ponieważ JavaScript obsługuje negatywne lookahead , jednym ze sposobów jest:
odwrócić ciąg wejściowy
dopasować z odwróconym wyrażeniem regularnym
odwrócić i sformatować dopasowania
Przykład 1:
Po pytaniu @ Andrew-Ensleya:
Wyjścia:
Przykład 2:
Po komentarzu @neaumusic (pasuje,
max-height
ale nieline-height
, token jestheight
):Wyjścia:
źródło
max-height
ale nieline-height
chcę i chcę tylko, aby mecz byłheight
''(?!\()
zastąpi apostrofy''(''test'''''''test
z drugiego końca, pozostawiając w ten sposób(''test'NNNtest
zamiast(''testNNN'test
.Załóżmy, że chcesz znaleźć wszystkie
int
nie poprzedzone przezunsigned
:Z obsługą negatywnego spojrzenia do tyłu:
Bez obsługi negatywnego spojrzenia wstecz:
Zasadniczo chodzi o to, aby pobrać n poprzedzających znaków i wykluczyć dopasowanie z ujemnym wyprzedzeniem, ale także dopasować przypadki, w których nie ma poprzedzających n znaków. (gdzie n jest długością patrzenia do tyłu).
A więc odnośne wyrażenie regularne:
przetłumaczyłoby się na:
Być może będziesz musiał bawić się grupami przechwytującymi, aby znaleźć dokładne miejsce struny, która Cię interesuje, lub chcesz zastąpić określoną część czymś innym.
źródło
"So it would match the 'm' in 'jim' or 'm', but not 'jam'".replace(/(j(?!([abcdefg])).|^)m/g, "$1[MATCH]")
zwroty"So it would match the 'm' in 'ji[MATCH]' or 'm', but not 'jam'"
To całkiem proste i działa!Strategia Mijoja działa w Twoim konkretnym przypadku, ale nie ogólnie:
Oto przykład, w którym celem jest dopasowanie podwójnego l, ale nie, jeśli jest ono poprzedzone „ba”. Zwróć uwagę na słowo „balll” - prawdziwe spojrzenie w tył powinno było stłumić pierwsze 2 litry, ale dopasować drugą parę. Ale dopasowując pierwsze 2 l, a następnie ignorując to dopasowanie jako fałszywie dodatni, silnik wyrażeń regularnych kontynuuje pracę od końca tego dopasowania i ignoruje wszystkie znaki z fałszywie dodatnich.
źródło
Posługiwać się
źródło
newString
zawsze będzie równestring
. Skąd tyle głosów za?"Jim Jam Momm m".replace(/([abcdefg])?m/g, function($0, $1){ return $1 ? $0 : '[match]'; });
. Powinien powrócićJi[match] Jam Mo[match][match] [match]
. Ale pamiętaj również, że jak Jason wspomniał poniżej, może to zawieść w niektórych skrajnych przypadkach.Możesz zdefiniować grupę bez przechwytywania, negując swój zestaw znaków:
... które pasowałoby do wszystkich znaków
m
NIE poprzedzonych którąkolwiek z tych liter.źródło
(?:[^a-g]|^)m
. Zobacz regex101.com/r/jL1iW6/2, aby zapoznać się z przykładem działania.Oto, jak osiągnąłem
str.split(/(?<!^)@/)
dla Node.js 8 (który nie obsługuje lookbehind):Pracuje? Tak (niesprawdzony kod Unicode). Nieprzyjemny? Tak.
źródło
podążając za ideą Mijoja i czerpiąc z problemów ujawnionych przez JasonS, wpadłem na ten pomysł; trochę sprawdziłem, ale nie jestem pewien siebie, więc weryfikacja przez kogoś bardziej eksperta ode mnie w js regex byłaby świetna :)
moje osobiste wyniki:
zasadą jest wywołanie
checker
w każdym punkcie ciągu między dowolnymi dwoma znakami, ilekroć ta pozycja jest punktem początkowym:--- dowolny fragment wielkości co nie jest pożądane (tutaj
'ba'
, w ten sposób..
) (jeśli rozmiar jest znany, w przeciwnym razie musi to być trudniejsze do zrobienia chyba)--- --- lub mniejsza, jeśli to początek ciągu:
^.?
a następnie
--- czego faktycznie należy szukać (tutaj
'll'
).Przy każdym wywołaniu funkcji
checker
będzie test sprawdzający, czy poprzednia wartośćll
nie jest tym, czego nie chcemy (!== 'ba'
); jeśli tak jest, wywołujemy inną funkcję i będzie to ta (doer
), która wprowadzi zmiany na str, jeśli celem jest ten lub bardziej ogólnie, wprowadzi dane niezbędne do ręcznego przetworzenia wyniki skanowaniastr
.tutaj zmieniamy ciąg, więc musieliśmy zachować ślad różnicy długości, aby zrównoważyć lokalizacje podane przez
replace
, wszystkie obliczone na podstawiestr
, które same się nigdy nie zmieniają.ponieważ ciągi pierwotne są niezmienne, moglibyśmy użyć zmiennej
str
do przechowywania wyniku całej operacji, ale pomyślałem, że przykład, już skomplikowany przez zamiany, byłby bardziej przejrzysty z inną zmienną (str_done
).wydaje mi się, że jeśli chodzi o występy, to musi być dość surowe: wszystkie te bezsensowne zamiany
this str.length-1
czasów `` na '' plus tutaj ręczna wymiana przez wykonawcę, co oznacza dużo krojenia ... prawdopodobnie w tym konkretnym przypadku, który mógłby być zgrupowane, przecinając sznurek tylko raz na kawałki wokół miejsca, w którym chcemy wstawić,[match]
i.join()
łącząc go ze[match]
sobą.Inną rzeczą jest to, że nie wiem, jak poradziłby sobie z bardziej złożonymi przypadkami, to znaczy ze złożonymi wartościami dla fałszywego lookbehind ... długość jest prawdopodobnie najbardziej problematycznymi danymi do uzyskania.
a
checker
w przypadku wielu możliwości niepotrzebnych wartości dla $ za nimi, będziemy musieli wykonać test z jeszcze innym wyrażeniem regularnym (checker
najlepiej buforowanym (utworzonym) na zewnątrz , aby uniknąć tworzenia tego samego obiektu wyrażenia regularnego na każde wezwaniechecker
), aby wiedzieć, czy jest to to, czego staramy się unikać.mam nadzieję, że wyraziłem się jasno; jeśli nie, nie wahaj się, spróbuję lepiej. :)
źródło
Używając swojej wielkości liter, jeśli chcesz
m
coś zastąpić , np. Zamienić na wielkie literyM
, możesz zanegować zbiór w grupie przechwytywania.dopasuj
([^a-g])m
, zamień na$1M
([^a-g])
dopasuje dowolny znak nie (^
) wa-g
zakresie i zapisze go w pierwszej grupie przechwytywania, aby można było uzyskać do niego dostęp za pomocą$1
.Więc znaleźć
im
wjim
i zastąpić goiM
co skutkujejiM
.źródło
Jak wspomniano wcześniej, JavaScript umożliwia teraz lookbehinds. W starszych przeglądarkach nadal potrzebujesz obejścia.
Założę się, że nie ma sposobu, aby znaleźć wyrażenie regularne bez lookbehind, które zapewnia dokładnie wynik. Wszystko, co możesz zrobić, to pracować z grupami. Załóżmy, że masz wyrażenie regularne
(?<!Before)Wanted
, gdzieWanted
jest wyrażenie regularne, które chcesz dopasować, aBefore
jest to wyrażenie regularne obliczające to, co nie powinno poprzedzać dopasowania. Najlepsze, co możesz zrobić, to zanegować wyrażenie regularneBefore
i użyć wyrażenia regularnegoNotBefore(Wanted)
. Pożądany wynik to pierwsza grupa$1
.W twoim przypadku,
Before=[abcdefg]
który łatwo zanegowaćNotBefore=[^abcdefg]
. Więc regex będzie[^abcdefg](m)
. Jeśli potrzebujesz pozycjiWanted
, musiszNotBefore
również zgrupować , aby pożądany wynik był drugą grupą.Jeśli dopasowania
Before
wzorca mają stałą długośćn
, to znaczy, jeśli wzorzec nie zawiera powtarzających się tokenów, możesz uniknąć negowaniaBefore
wzorca i użyć wyrażenia regularnego(?!Before).{n}(Wanted)
, ale nadal musisz użyć pierwszej grupy lub użyć wyrażenia regularnego(?!Before)(.{n})(Wanted)
i użyć drugiej Grupa. W tym przykładzie wzorzecBefore
ma w rzeczywistości stałą długość, a mianowicie 1, więc użyj wyrażenia regularnego(?![abcdefg]).(m)
lub(?![abcdefg])(.)(m)
. Jeśli interesują Cię wszystkie dopasowania, dodajg
flagę, zobacz mój fragment kodu:źródło
To skutecznie to robi
Wyszukaj i zamień przykład
Zwróć uwagę, że ujemny ciąg znaków ostrzegawczych musi mieć 1 znak, aby to zadziałało.
źródło
"m".match(/[^a-g]m/)
yeildsnull
również. W tym przypadku też chcę mieć „m”./(?![abcdefg])[^abcdefg]m/gi
tak to jest sztuczka.źródło
(?![abcdefg])
jest całkowicie zbędne, ponieważ[^abcdefg]
już wykonuje swoje zadanie, aby zapobiec dopasowaniu tych znaków.