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.