javascript regex - szukasz alternatywy?


143

Oto wyrażenie regularne, które działa dobrze w większości implementacji wyrażeń regularnych:

(?<!filename)\.js$

To dopasowuje .js do łańcucha, który kończy się na .js, z wyjątkiem nazwy pliku.js

Javascript nie zawiera lookbehind wyrażenia regularnego. Czy ktoś jest w stanie złożyć alternatywne wyrażenie regularne, które osiąga ten sam wynik i działa w javascript?

Oto kilka myśli, ale potrzebne są funkcje pomocnicze. Miałem nadzieję, że uda mi się to osiągnąć za pomocą wyrażenia regularnego: http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript


3
jeśli chcesz tylko sprawdzić konkretną nazwę pliku lub listę nazw plików, dlaczego nie skorzystać po prostu z dwóch sprawdzeń? sprawdź, czy kończy się na .js, a jeśli tak, sprawdź, czy nie pasuje do nazwy pliku.js lub odwrotnie.
si28719e

3
Aktualizacja: najnowsza publiczna wersja Chrome (v62) zawiera (prawdopodobnie eksperymentalną) lookbehinds po wyjęciu z pudełka: D Pamiętaj jednak, że lookbehinds są nadal w fazie propozycji 3: github.com/tc39/proposal-regexp-lookbehind . Tak więc może minąć trochę czasu, zanim JavaScript wszędzie ją obsłuży. Lepiej uważaj podczas używania w produkcji!
Eirik Birkeland

2
# Aktualizacja: ES2018 zawiera asercje lookbehind Plus : - tryb dotAll (flaga s) - asercje Lookbehind - nazwane grupy przechwytywania - ucieczki właściwości Unicode
Ashley Coolman

2
Wystarczy użyć (?<=thingy)thingydo pozytywnego lookbehind a (?<!thingy)thingydla ujemnej lookbehind . Teraz je wspiera.
Константин Ван

7
@ K._ Od lutego 2018 r. To jeszcze nie prawda !! I zajmie to trochę czasu, ponieważ przeglądarki i silniki muszą implementować specyfikację (obecnie w wersji roboczej).
Andre Figueiredo,

Odpowiedzi:


64

^(?!filename).+\.js pracuje dla mnie

przetestowany pod kątem:

  • test.js pasuje
  • blabla.js pasuje
  • nazwa_pliku.js nie pasuje

Właściwe wyjaśnienie tego wyrażenia regularnego można znaleźć w Wyrażenie regularne, aby dopasować ciąg niezawierający słowa?

Funkcja Look ahead jest dostępna od wersji 1.5 javascript i jest obsługiwana przez wszystkie główne przeglądarki

Zaktualizowano, aby pasował do nazw plików2.js i 2nazwapliku.js, ale nie nazwapliku.js

(^(?!filename\.js$).).+\.js


5
Pytanie to zostało połączone do rozmów o nieco innym problemem: dopasowanie ciąg, który nie zawiera docelowego słowo nigdzie . Ten jest znacznie prostszy: dopasowywanie łańcucha, który nie zaczyna się od słowa docelowego.
Alan Moore,

To naprawdę fajne, pomija tylko przypadki takie jak: nazwa_pliku2.js lub nazwa_plikudk.js lub podobne. To nie pasuje, ale powinno pasować.
daniel,

9
@daniel Poprosiłeś o spojrzenie za siebie, a nie o spojrzenie w przyszłość, dlaczego zaakceptowałeś tę odpowiedź?
hek2mgl

1
ten podany nie pasuje naa.js
inetphantom

1
Oryginalne wyrażenie regularne z lookbehind nie pasuje 2filename.js, ale podane tutaj wyrażenie regularne tak. Byłby bardziej odpowiedni ^(?!.*filename\.js$).*\.js$. Oznacza to, dopasuj dowolny *.js z wyjątkiem *filename.js .
weibeld

153

EDYCJA: od ECMAScript 2018 asercje lookbehind (nawet bez ograniczeń) są obsługiwane natywnie .

W poprzednich wersjach możesz to zrobić:

^(?:(?!filename\.js$).)*\.js$

Robi to jawnie to, co robi niejawnie wyrażenie lookbehind: sprawdź każdy znak ciągu, jeśli wyrażenie lookbehind oraz wyrażenie regularne po nim nie będą pasować, i tylko wtedy pozwól temu znakowi na dopasowanie.

^                 # Start of string
(?:               # Try to match the following:
 (?!              # First assert that we can't match the following:
  filename\.js    # filename.js 
  $               # and end-of-string
 )                # End of negative lookahead
 .                # Match any character
)*                # Repeat as needed
\.js              # Match .js
$                 # End of string

Kolejna edycja:

Boli mnie, gdy mówię (zwłaszcza, że ​​ta odpowiedź została tak bardzo pozytywnie oceniona), że istnieje znacznie łatwiejszy sposób na osiągnięcie tego celu. Nie ma potrzeby sprawdzania lookahead przy każdej postaci:

^(?!.*filename\.js$).*\.js$

działa równie dobrze:

^                 # Start of string
(?!               # Assert that we can't match the following:
 .*               # any string, 
  filename\.js    # followed by filename.js
  $               # and end-of-string
)                 # End of negative lookahead
.*                # Match any string
\.js              # Match .js
$                 # End of string

Działa w wielu przypadkach, z wyjątkiem sytuacji, gdy występują znaki poprzedzające, na przykład: nazwa_pliku.js (działa-nomatch) nazwa_pliku2.js (pasuje-działa) blah.js (działa - dopasowuje) 2filename.js (nie działa - nomatch) --- po powiedziawszy, lookbehind ma takie same ograniczenia, które nie zdawałem sobie sprawy aż do teraz ...
Daniel

9
@daniel: Cóż, twoje wyrażenie regularne (z lookbehind) również nie pasuje 2filename.js. Moje wyrażenie regularne pasuje dokładnie w tych samych przypadkach, co przykładowe wyrażenie regularne.
Tim Pietzcker,

Wybacz moją naiwność, ale czy jest tu pożytek dla grupy, która nie przechwytuje? Zawsze wiedziałem, że jest to przydatne tylko wtedy, gdy próbuję zebrać odwołanie zwrotne do zastąpienia w ciągu. O ile wiem, to też zadziała ^ (?! nazwa_pliku \ .js $). * \. Js $
I Want Answers

1
Niezupełnie, to wyrażenie regularne sprawdza „filename.js” tylko na początku łańcucha. Ale ^(?!.*filename\.js$).*\.js$zadziała. Próbując pomyśleć o sytuacjach, w których grupa nc może być nadal potrzebna ...
Tim Pietzcker

To podejście można podsumować następująco: zamiast patrzeć za X, spójrz przed siebie na każdą postać, która znajduje się przed X?
Sarsaparilla

25

Załóżmy, że chcesz znaleźć wszystkie intnie poprzedzone przez unsigned:

Z obsługą negatywnego spojrzenia do tyłu:

(?<!unsigned )int

Bez obsługi negatywnego spojrzenia wstecz:

((?!unsigned ).{9}|^.{0,8})int

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:

(?<!filename)\.js$

przetłumaczyłoby się na:

((?!filename).{8}|^.{0,7})\.js$

Być może będziesz musiał bawić się grupami przechwytującymi, aby znaleźć dokładne miejsce struny, która Cię interesuje, lub nie chcesz zastąpić określonej części czymś innym.


Właśnie przekonwertowałem to: (?<!barna)(?<!ene)(?<!en)(?<!erne) (?:sin|vår)e?(?:$| (?!egen|egne))na (?!barna).(?!erne).(?!ene).(?!en).. (?:sin|vår)e?(?:$| (?!egen|egne))co działa na moje potrzeby. Przedstawiam to jako kolejny scenariusz „w świecie rzeczywistym”. Zobacz link
Eirik Birkeland

Myślę, że miałeś na myśli:((?!unsigned ).{9}|^.{0,8})int
pansay

@pansay Yes. Dziękuję Ci. Właśnie poprawiłem swoją odpowiedź.
Kamil Szot

2
Dzięki za bardziej uogólnioną odpowiedź, która działa nawet wtedy, gdy istnieje potrzeba dopasowania w głębi tekstu (gdzie początkowe ^ byłoby niepraktyczne)!
Milos Mrdovic

5

Jeśli możesz patrzeć przed siebie, ale wstecz, możesz najpierw odwrócić ciąg, a następnie spojrzeć w przód. Oczywiście trzeba będzie wykonać więcej pracy.


8
Ta odpowiedź naprawdę przydałaby się poprawa. Wydaje mi się to raczej komentarzem.
mickmackusa

2

To jest odpowiednik odpowiedzi Tima Pietzckera (zobacz także komentarze do tej samej odpowiedzi):

^(?!.*filename\.js$).*\.js$

To znaczy dopasuj *.jsz wyjątkiem *filename.js.

Aby dostać się do tego rozwiązania, możesz sprawdzić, które wzorce wyklucza negatywne lookbehind, a następnie wykluczyć dokładnie te wzorce za pomocą negatywnego lookbehind.


-1

Poniżej znajduje się pozytywna alternatywa dla JavaScript, pokazująca, jak przechwytywać nazwiska osób z „Michaelem” jako imieniem.

1) Biorąc pod uwagę ten tekst:

const exampleText = "Michael, how are you? - Cool, how is John Williamns and Michael Jordan? I don't know but Michael Johnson is fine. Michael do you still score points with LeBron James, Michael Green Miller and Michael Wood?";

pobierz tablicę nazwisk osób o imieniu Michael. Wynik powinien być:["Jordan","Johnson","Green","Wood"]

2) Rozwiązanie:

function getMichaelLastName2(text) {
  return text
    .match(/(?:Michael )([A-Z][a-z]+)/g)
    .map(person => person.slice(person.indexOf(' ')+1));
}

// or even
    .map(person => person.slice(8)); // since we know the length of "Michael "

3) Sprawdź rozwiązanie

console.log(JSON.stringify(    getMichaelLastName(exampleText)    ));
// ["Jordan","Johnson","Green","Wood"]

Demo tutaj: http://codepen.io/PiotrBerebecki/pen/GjwRoo

Możesz także wypróbować, uruchamiając poniższy fragment kodu.

const inputText = "Michael, how are you? - Cool, how is John Williamns and Michael Jordan? I don't know but Michael Johnson is fine. Michael do you still score points with LeBron James, Michael Green Miller and Michael Wood?";



function getMichaelLastName(text) {
  return text
    .match(/(?:Michael )([A-Z][a-z]+)/g)
    .map(person => person.slice(8));
}

console.log(JSON.stringify(    getMichaelLastName(inputText)    ));

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.