Wyrażenie regularne dla ciągu cytowanego w cudzysłowie

122

Jak uzyskać podciąg " It's big \"problem "przy użyciu wyrażenia regularnego?

s = ' function(){  return " It\'s big \"problem  ";  }';     
David
źródło
1
Jak znaleźć „To” w ciągu zawierającym tylko „Jest”? Naprawiłbym to za Ciebie, ale nie wiem, które konwencje pojedynczych cudzysłowów / zmiany znaczenia mają zastosowanie w języku, którego używasz.
Jonathan Leffler
2
Właściwie, patrząc na daty, widzę, że drugie pytanie jest duplikatem tego. Tak czy inaczej, koniecznie sprawdź moją odpowiedź .
ridgerunner
@ridgerunner: Głosuję za zamknięciem tego, jak sugerowałeś. To prawda, że ​​inne pytanie jest nowsze, ale jest też znacznie lepsze (głównie dzięki twojej odpowiedzi).
Alan Moore,

Odpowiedzi:

160
/"(?:[^"\\]|\\.)*"/

Działa w The Regex Coach i PCRE Workbench.

Przykład testu w JavaScript:

    var s = ' function(){ return " Is big \\"problem\\", \\no? "; }';
    var m = s.match(/"(?:[^"\\]|\\.)*"/);
    if (m != null)
        alert(m);

PhiLho
źródło
24
Ma sens. Zwykły angielski: dwa cudzysłowy otaczające zero lub więcej „dowolnego znaku, który nie jest cudzysłowem ani ukośnikiem odwrotnym” lub „ukośnikiem odwrotnym, po którym następuje dowolny znak”. Nie mogę uwierzyć, że nie pomyślałem, żeby to zrobić ...
Ajedi, 32
7
Odpowiem sobie. =) (?:...)to grupa pasywna lub nieprzechwytująca. Oznacza to, że nie można później odwołać się do niego.
magras
po wielu poszukiwaniach i testach jest to prawdziwe i jedyne rozwiązanie tego powszechnego problemu. Dzięki!
cancerbero
10
dzięki za to. chciałem również dopasować pojedyncze cytaty, więc ostatecznie dostosowałem to do tego:/(["'])(?:[^\1\\]|\\.)*?\1/
leo
W przypadku var s = ' my \\"new\\" string and \"this should be matched\"';tego podejścia przyniesie to nieoczekiwane rezultaty.
Wiktor Stribiżew
32

Ten pochodzi z nanorc.sample dostępnej w wielu dystrybucjach Linuksa. Służy do podświetlania składni napisów w stylu C.

\"(\\.|[^\"])*\"

źródło
W przypadku var s = ' my \\"new\\" string and \"this should be matched\"';tego podejścia przyniesie to nieoczekiwane rezultaty.
Wiktor Stribiżew
1
c.nanorc był pierwszym miejscem, do którego pojechałem. Nie mogłem zmusić go do działania jako części dosłownego ciągu C, dopóki podwójnie nie ucieknie wszystkiego takiego" \"(\\\\.|[^\\\"])*\" "
hellork
Działa to z funkcjami egrep i re_comp / re_exec z biblioteki libc.
fk0
19

Jak zapewnia ePharaoh, odpowiedź brzmi

/"([^"\\]*(\\.[^"\\]*)*)"/

Aby powyższe odnosiło się do ciągów w pojedynczych lub podwójnych cudzysłowach, użyj

/"([^"\\]*(\\.[^"\\]*)*)"|\'([^\'\\]*(\\.[^\'\\]*)*)\'/
Guy Bedford
źródło
2
Jest to jedyny zestaw, który działał dla mnie z pojedynczym, dużym ciągiem cytowanym o rozmiarze 1,5 KB, zawierającym 99 znaków specjalnych. Każde inne wyrażenie na tej stronie zepsuło się w moim edytorze tekstu z powodu przepełnienia. Chociaż większość z nich działa w przeglądarce, tylko o czym należy pamiętać. Fiddle: jsfiddle.net/aow20y0L
Beejor
3
Zobacz odpowiedź @ MarcAndrePoulin poniżej, aby uzyskać wyjaśnienie.
shaunc
10

Większość przedstawionych tutaj rozwiązań wykorzystuje alternatywne ścieżki powtórzeń, np. (A | B) *.

Możesz napotkać przepełnienia stosu na dużych danych wejściowych, ponieważ niektóre kompilatory wzorców implementują to przy użyciu rekurencji.

Na przykład Java: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6337993

Coś takiego: "(?:[^"\\]*(?:\\.)?)*"lub ten dostarczony przez Guya Bedforda zmniejszy liczbę kroków parsowania, unikając większości przepełnień stosu.

Marc-André Poulin
źródło
7
/"(?:[^"\\]++|\\.)*+"/

Zaczerpnięte bezpośrednio z man perlresystemu Linux z zainstalowanym Perlem 5.22.0. Jako optymalizacja, to wyrażenie regularne używa „dodatniej” formy obu +i *zapobiega cofaniu się, ponieważ wiadomo z góry, że ciąg bez cudzysłowu zamykającego w żadnym wypadku nie pasowałby .

ACK
źródło
4
/(["\']).*?(?<!\\)(\\\\)*\1/is

powinien działać z dowolnym ciągiem w cudzysłowie


źródło
1
Ładnie, ale zbyt elastycznie dla żądania (dopasuje pojedyncze cudzysłowy ...). I można to uprościć do /".*?(?<!\)"/, chyba że coś przeoczę. Aha, i niektóre języki (np. JavaScript) niestety nie rozumieją negatywnych wyrażeń typu lookbehind.
PhiLho
1
@PhiLho, samo użycie pojedynczego (? <! \\) zakończy się niepowodzeniem w przypadku odwrotnych ukośników uciekających na końcu ciągu. Jednak prawda o look-backach w JavaScript.
Markus Jarderot
4

Ten działa idealnie na PCRE i nie spada z StackOverflow.

"(.*?[^\\])??((\\\\)+)?+"

Wyjaśnienie:

  1. Każdy cudzysłowie rozpoczyna Char: ";
  2. Może zawierać dowolną liczbę dowolnych znaków: .*?{Lazy match}; kończące się znakiem bez zmiany znaczenia [^\\];
  3. Instrukcja (2) jest opcjonalna Lazy (!), Ponieważ łańcuch może być pusty („”). Więc:(.*?[^\\])??
  4. Wreszcie każdy cytowany ciąg kończy się "znakiem Char ( ), ale można go poprzedzić parzystą liczbą par znaków ucieczki (\\\\)+; i jest Greedy (!) opcjonalne: ((\\\\)+)?+{Greedy matching}, ponieważ łańcuch może być pusty lub bez końcowych par!
Vadim Sayfi
źródło
Nie jest to najbardziej efektywny wzór na świecie, ale pomysł jest interesujący. Zauważ, że możesz to skrócić w ten sposób:"(.*?[^\\])?(\\\\)*"
Casimir et Hippolyte
2

tutaj jest taki, który działa zarówno z ", jak i", a na początku możesz łatwo dodać inne.

("| ') (?: \\\ 1 | [^ \ 1]) *? \ 1

używa odwołania wstecznego (\ 1) do dokładnego dopasowania tego, co jest w pierwszej grupie („lub”).

http://www.regular-expressions.info/backref.html

mathias hansen
źródło
jest to bardzo dobre rozwiązanie, ale [^\1]należy je zastąpić, .ponieważ nie ma czegoś takiego jak antyreferencja, a to i tak nie ma znaczenia. pierwszy warunek zawsze będzie pasował, zanim zdarzy się coś złego.
Seph Reed
@SephReed - zastąpienie [^\1]przez .skutecznie zmieniłoby to wyrażenie regularne na, ("|').*?\1a następnie pasowałoby "foo\"do "foo \" bar". To powiedziawszy, [^\1]trudno jest naprawdę pracować. @ Mathiashansen - Lepiej jest, jeśli masz nieporęczny i drogi (?!\1).(więc całe wyrażenie regularne, z pewnymi poprawkami wydajności, byłoby (["'])(?:\\.|(?!\1).)*+\1. +Jest opcjonalne, jeśli twój silnik go nie obsługuje.
Adam Katz
2

Opcja, która nie została wcześniej poruszona, to:

  1. Odwróć sznurek.
  2. Wykonaj dopasowanie na odwróconym łańcuchu.
  3. Ponownie odwróć dopasowane ciągi.

Ma to dodatkową zaletę polegającą na możliwości prawidłowego dopasowania niezamkniętych otwartych tagów.

Powiedzmy, że masz następujący ciąg; String \"this "should" NOT match\" and "this \"should\" match" Tutaj \"this "should" NOT match\"nie powinno być dopasowane, a "should"powinno być. Ponadto this \"should\" matchpowinny być dopasowane, a \"should\"nie powinny.

Najpierw przykład.

// The input string.
const myString = 'String \\"this "should" NOT match\\" and "this \\"should\\" match"';

// The RegExp.
const regExp = new RegExp(
    // Match close
    '([\'"])(?!(?:[\\\\]{2})*[\\\\](?![\\\\]))' +
    '((?:' +
        // Match escaped close quote
        '(?:\\1(?=(?:[\\\\]{2})*[\\\\](?![\\\\])))|' +
        // Match everything thats not the close quote
        '(?:(?!\\1).)' +
    '){0,})' +
    // Match open
    '(\\1)(?!(?:[\\\\]{2})*[\\\\](?![\\\\]))',
    'g'
);

// Reverse the matched strings.
matches = myString
    // Reverse the string.
    .split('').reverse().join('')
    // '"hctam "\dluohs"\ siht" dna "\hctam TON "dluohs" siht"\ gnirtS'

    // Match the quoted
    .match(regExp)
    // ['"hctam "\dluohs"\ siht"', '"dluohs"']

    // Reverse the matches
    .map(x => x.split('').reverse().join(''))
    // ['"this \"should\" match"', '"should"']

    // Re order the matches
    .reverse();
    // ['"should"', '"this \"should\" match"']

OK, teraz wyjaśnij RegExp. To jest wyrażenie regularne, które można łatwo podzielić na trzy części. Następująco:

# Part 1
(['"])         # Match a closing quotation mark " or '
(?!            # As long as it's not followed by
  (?:[\\]{2})* # A pair of escape characters
  [\\]         # and a single escape
  (?![\\])     # As long as that's not followed by an escape
)
# Part 2
((?:          # Match inside the quotes
(?:           # Match option 1:
  \1          # Match the closing quote
  (?=         # As long as it's followed by
    (?:\\\\)* # A pair of escape characters
    \\        # 
    (?![\\])  # As long as that's not followed by an escape
  )           # and a single escape
)|            # OR
(?:           # Match option 2:
  (?!\1).     # Any character that isn't the closing quote
)
)*)           # Match the group 0 or more times
# Part 3
(\1)           # Match an open quotation mark that is the same as the closing one
(?!            # As long as it's not followed by
  (?:[\\]{2})* # A pair of escape characters
  [\\]         # and a single escape
  (?![\\])     # As long as that's not followed by an escape
)

Jest to prawdopodobnie dużo jaśniejsze w formie obrazu: wygenerowane za pomocą Regulexa firmy Jex

Obraz na github (Wizualizator wyrażeń regularnych JavaScript.) Przepraszam, nie mam wystarczającej reputacji, aby dołączyć obrazy, więc na razie to tylko link.

Oto streszczenie przykładowej funkcji wykorzystującej tę koncepcję, która jest nieco bardziej zaawansowana: https://gist.github.com/scagood/bd99371c072d49a4fee29d193252f5fc#file-matchquotes-js

Scagood
źródło
0

Należy pamiętać, że wyrażenia regularne nie są srebrną kulą dla wszystkiego, co ciągłe. Niektóre rzeczy są prostsze przy użyciu kursora i liniowego, ręcznego wyszukiwania. CFL by rade dość trywialnie, ale nie ma wielu implementacje CFL (AFAIK).

Henrik Paul
źródło
3
To prawda, ale ten problem mieści się w zakresie możliwości wyrażeń regularnych i istnieje wiele ich implementacji.
Alan Moore
0

Bardziej rozbudowana wersja https://stackoverflow.com/a/10786066/1794894

/"([^"\\]{50,}(\\.[^"\\]*)*)"|\'[^\'\\]{50,}(\\.[^\'\\]*)*\'|“[^”\\]{50,}(\\.[^“\\]*)*”/   

Ta wersja zawiera również

  1. Minimalna długość oferty to 50
  2. Dodatkowe rodzaje ofert (otwieranie i zamykanie )
Rvanlaak
źródło
0

Pomieszane w regexpal i skończyło się na tym wyrażeniu regularnym: (Nie pytaj mnie, jak to działa, ledwo rozumiem, nawet jeśli napisałem to lol)

"(([^"\\]?(\\\\)?)|(\\")+)+"
Petter Thowsen
źródło
0

Jeśli jest szukany od początku, może to zadziała?

\"((\\\")|[^\\])*\"
user2267983
źródło
0

Podobny problem napotkałem, próbując usunąć cytowane w cudzysłowie ciągi znaków, które mogą przeszkadzać w analizowaniu niektórych plików.

Skończyło się na dwuetapowym rozwiązaniu, które pokonuje wszelkie zawiłe wyrażenia regularne, jakie możesz wymyślić:

 line = line.replace("\\\"","\'"); // Replace escaped quotes with something easier to handle
 line = line.replaceAll("\"([^\"]*)\"","\"x\""); // Simple is beautiful

Łatwiejsze do odczytania i prawdopodobnie bardziej wydajne.

マ ル ち ゃ ん だ よ
źródło
0

Jeśli Twoim IDE jest IntelliJ Idea, możesz zapomnieć o wszystkich tych bólach głowy i zapisać swoje wyrażenie regularne w zmiennej typu String, a podczas kopiowania i wklejania go w podwójnym cudzysłowie automatycznie zmieni się na akceptowalny format wyrażenia regularnego.

przykład w Javie:

String s = "\"en_usa\":[^\\,\\}]+";

teraz możesz użyć tej zmiennej w swoim wyrażeniu regularnym lub gdziekolwiek.

Aramis NSR
źródło