2012-03-15 16 views
7

Chcę skonstruować wyrażenie regularne, które pasuje albo ' lub ", a następnie dopasowuje inne znaki, kończące się dopasowaniem, odpowiednio, ' lub ", w zależności od tego, co napotkano na samym początku początek. Problem ten wydaje się więc na tyle prosty, że można go rozwiązać przy pomocy odwołania zwrotnego na końcu; Oto niektóre kodu regex poniżej (to w Javie więc przeszkadza dodatkowe znaki ewakuacyjne, takie jak \ przed "):Regex; backreferencing znak, który NIE był dopasowany w zestawie znaków

private static String seekerTwo = "(['\"])([a-zA-Z])([a-zA-Z0-9():;/`\\=\\.\\,\\- ]+)(\\1)"; 

Ten kod skutecznie radzić sobie z rzeczy, takich jak:

"hello my name is bob" 
'i live in bethnal green' 

The Problem pojawia się, gdy mam ciąg jak poniżej:

"hello this seat 'may be taken' already" 

Stosując powyższe regex na nie powiedzie się w początkowej części po napotkaniu ' to będzie kontynuować i pomyślnie dopasować 'may be taken' ... ale jest to oczywiście niewystarczające, potrzebuję dopasować cały ciąg.

Myślę, że potrzebuję sposobu, aby zignorować typ cudzysłowu, który nie był dopasowany w pierwszej grupie, włączając go jako znak w zestawie znaków trzeciej grupy. Jednak nie wiem, jak to zrobić. Czy istnieje jakaś podstępna funkcja NOT backreference czy coś takiego? Coś, czego mogę użyć, aby odnieść się do postaci z 1. grupy, która NIE była dopasowana? Lub w inny sposób jakieś rozwiązanie mojego kłopotliwego położenia?

+0

Witam i witam w StackOverflow. Pozwoliłem trochę sformatować twoją pocztę. Możesz kliknąć link edycji, aby zobaczyć, jak to zrobiłem. Bardzo ważne jest, aby wiedzieć, czy musisz opublikować kod ... –

Odpowiedz

12

Można to zrobić, używając negatywu lookahead assertions. Poniższy rozwiązanie nawet uwzględnia fakt, że można uciec cytat wewnątrz ciągu znaków:

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

wyjaśnienie:

(["']) # Match and remember a quote. 
(?:  # Either match... 
\\.  # an escaped character 
|   # or 
(?!\1) # (unless that character is identical to the quote character in \1) 
.  # any character 
)*  # any number of times. 
\1  # Match the corresponding quote. 

To właściwie pasuje "hello this seat 'may be taken' already" lub "hello this seat \"may be taken\" already".

W Javie wszystkie ukośniki:

Pattern regex = Pattern.compile(
    "([\"']) # Match and remember a quote.\n" + 
    "(?:  # Either match...\n" + 
    " \\\\. # an escaped character\n" + 
    "|   # or\n" + 
    " (?!\\1) # (unless that character is identical to the matched quote char)\n" + 
    " .  # any character\n" + 
    ")*  # any number of times.\n" + 
    "\\1  # Match the corresponding quote", 
    Pattern.COMMENTS); 
+0

+1 za dobrze przemyślane i wyjaśnione rozwiązanie! – FloppyDisk

+0

Doskonała praca tam Tim, i dziękuję za edycję mojego posta. Dzięki twojej sugestii, przy odrobinie pracy, zmodyfikowałem swój kod w następujący sposób: "(['\"]) ([a-zA-Z]) ((?! \\ 1) [a-zA-Z0-9():;/''\" \\ = \\. \\, \\ -]) + (\\ 1) ", więc twoje rozwiązanie było wystarczająco proste i doskonale skuteczne; dodaj equivelent do wyrażenia regularnego, jeśli przed głównym zestawem znaków zostanie pominięte prawo do ostatniej pętli. Dodaj oba typy cudzysłowów do głównego zestawu znaków. W ten sposób, jeśli w dowolnym momencie zostanie znaleziony znak "znajdź na początku", wyrażenie regularne zakończy się i wróci. Miły. –

2

rozwiązanie Tima działa dość dobrze, jeśli można użyć lookaround (który obsługuje Java). Ale czy powinien znaleźć się za pomocą języka lub narzędzie, które nie obsługuje lookaround, można po prostu pasuje do obu przypadkach (podwójne notowane ciągi i pomiędzy pojedynczymi cudzysłowami) oddzielnie:

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

mecze każdy przypadek osobno, ale zwraca obu przypadkach jak cały mecz


JEDNAK

Oba przypadki mogą paść ofiarą przynajmniej jedną ewentualność. Jeśli nie przyjrzeć się bliżej, można pomyśleć, nie powinno być dwa mecze w tym fragmencie:

odwrócił się, aby na swoim rowerze."Do zobaczenia później, kiedy skończę z tym wszystkim" - powiedział, spoglądając na chwilę przed rozpoczęciem podróży. Gdy wszedł na ulicę, jeden z miejskich wózków zderzył się z rowerem Mike'a. "O mój!" - wykrzyknął obserwator.

... ale istnieją trzy mecze, nie dwa:

"I'll see you later, when I'm done with all this" 
's trolleys collided with Mike' 
"Oh my!" 

i ten fragment zawiera tylko ONE meczu:

Walka nie była jeszcze zakończona , chociaż. "Hej!" wrzasnął Bob. "Co chcesz?" Odparłem. "Nienawidzę twoich wnętrzności!" "Dlaczego miałoby mnie to obchodzić?" "Ponieważ cię kocham!" "Ty robisz?" Bob przerwał na chwilę, szepcząc "Nie, nie mogłem cię kochać!"

możesz go znaleźć? : D

't over yet, though. "Hey!" yelled Bob. "What do you want?" I retorted. "I hate your guts!" "Why would I care?" "Because I love you!" "You do?" Bob paused for a moment before whispering "No, I couldn' 

polecam (jeśli się za korzystanie lookaround), że w przyszłości jakiejś dodatkowej kontroli (takich jak pozytywny lookbehind dla spacji lub podobny przed pierwszym cytat), aby upewnić się, że don” t pasują rzeczy takie jak 's trolleys collided with Mike' - chociaż nie będę wkładał dużo pieniędzy na żadne rozwiązanie bez wcześniejszych testów. Dodawanie (?<=\s|^) na początku każdej wypowiedzi uniknie powyższe przypadki ... czyli:

(?<=\s|^)(["'])(?:\\.|(?!\1).)*\1     #based on Tim's 

lub

(?<=\s|^)("(\\"|[^"])*"|'(\\'|[^'])*')    #based on my alternative 

Nie jestem pewien, jak wydajny lookaround porównywana jest non-lookaround, więc dwa powyższe mogą być równoważne lub jeden może być bardziej wydajny niż inny (?)

+0

Kilka dobrych punktów tutaj Code Jockey, a nawet przetwarzanie tekstu w języku angielskim w ten sposób nie byłoby rozsądne. Jednak próbuję faktycznie parsować rosyjski tekst w kodzie MySQL (zmieniłem kod-яА-ЯёЁ na a-zA-Z w powyższym kodzie, aby ludzie tutaj byli w stanie zrozumieć znaczenie), i podczas analizowania łańcuchów w kodzie są oczywiście zawsze gwarantowane, że zostaną ujęte w jednym typie znaku cudzysłowu lub w innym. –

Powiązane problemy