2009-03-10 17 views
32

Nie jestem do końca pewien, czy to możliwe, więc zwracam się do Ciebie.Regex, aby wybrać przecinki poza cudzysłowami

Chciałbym znaleźć wyrażenie regularne, które wybierze wszystkie przecinki, które nie mieszczą się w zestawach cytatów.

Na przykład:

'foo' => 'bar', 
'foofoo' => 'bar,bar' 

To wyłowić jeden przecinek na linii 1, po 'bar',

nie troszczą się o pojedynczy vs cudzysłowach.

Czy ktoś ma jakieś myśli? Czuję, że powinno to być możliwe z readaheads, ale moje regex fu jest zbyt słabe.

+3

Oficjalna ostateczna odpowiedź, podana przez MarkusQ: (,) (? = (?: [^ "'] | [" |'] [^ "'] *") * $) – SocialCensus

+0

Na marginesie to nie działa w przypadku przerw w linii środkowej. – SocialCensus

+0

@SocialCensus zobacz moją notatkę dołączoną do odpowiedzi. – MarkusQ

Odpowiedz

74

Spowoduje to dopasowanie dowolnego ciągu znaków do pierwszego niecałkowitego "," włącznie. Czy tego właśnie chcesz?

/^([^"]|"[^"]*")*?(,)/ 

Jeśli chcesz wszystkie z nich (i jako kontrprzykład do faceta, który powiedział, że nie było to możliwe) można napisać:

/(,)(?=(?:[^"]|"[^"]*")*$)/ 

które pasują do wszystkich z nich. Zatem

'test, a "comma,", bob, ",sam,",here'.gsub(/(,)(?=(?:[^"]|"[^"]*")*$)/,';') 

zastępuje wszystkie przecinki nie wewnątrz cytaty z średnikiem i produkuje:

'test; a "comma,"; bob; ",sam,";here' 

Jeśli jest to potrzebne do pracy w poprzek linii przerwy wystarczy dodać flagę m (multiline).

+0

Wygląda na to, że działa poprawnie - z podwójnymi cudzysłowami. (,) (? = (?: [^ "'] | [" |'] [^ "'] *") * $) Wierzę, że działa z pojedynczym cudzysłowem lub podwójnymi cudzysłowami. Dzięki! – SocialCensus

+1

Chciałem podkreślić, że nie działa to w przypadku podziałów linii. – SocialCensus

+0

@SocialCensus Następnie użyj flagi m. Twój przykład w powyższym komentarzu zawiera kilka błędów. Na przykład, podwójne cudzysłowy, pojedyncze cudzysłowie i pionowe słupki służą do otwierania cudzysłowów, ale uwzględniają je tylko jako cudzysłowy zamykające. – MarkusQ

1

Spróbuj tego wyrażenia regularnego:

(?:"(?:[^\\"]+|\\(?:\\\\)*[\\"])*"|'(?:[^\\']+|\\(?:\\\\)*[\\'])*')\s*=>\s*(?:"(?:[^\\"]+|\\(?:\\\\)*[\\"])*"|'(?:[^\\']+|\\(?:\\\\)*[\\'])*')\s*, 

ta ma również umożliwić ciągi jak „'foo\'bar' => 'bar\\',”.

+0

Ten nie wydaje się działać dla mnie ... – SocialCensus

1

Odpowiedź MarkusQ sprawdziła się znakomicie przez około rok, aż do tego czasu. Właśnie dostałem błąd przepełnienia stosu w linii z około 120 przecinkami i 3682 znakami łącznie. W Javie, tak:

 String[] cells = line.split("[\t,](?=(?:[^\"]|\"[^\"]*\")*$)", -1); 

Oto mój bardzo nieeleganckie zastępczą, która nie przepełnienie stosu:

private String[] extractCellsFromLine(String line) { 
    List<String> cellList = new ArrayList<String>(); 
    while (true) { 
     String[] firstCellAndRest; 
     if (line.startsWith("\"")) { 
      firstCellAndRest = line.split("([\t,])(?=(?:[^\"]|\"[^\"]*\")*$)", 2); 
     } 
     else { 
      firstCellAndRest = line.split("[\t,]", 2);     
     } 
     cellList.add(firstCellAndRest[0]); 
     if (firstCellAndRest.length == 1) { 
      break; 
     } 
     line = firstCellAndRest[1]; 
    } 
    return cellList.toArray(new String[cellList.size()]); 
} 
1

@SocialCensus, przykład dałeś w komentarzu do MarkusQ, gdzie można rzucać w ' obok ", nie działa z przykładem MarkusQ nadanym tuż powyżej, jeśli zmienimy sam na sam's: (test," przecinek ", bob,", sam ", tutaj) nie ma odpowiednika (,) (? = (?: [^ "'] | [" |'] [^ "'] ") $.) W rzeczywistości problem i "Nie interesuje mnie ani jedno, ani podwójne cytaty", jest niejednoznaczne. Musisz być jasne, co masz na myśli, cytując albo z "albo z." Na przykład, czy zagnieżdżanie jest dozwolone czy nie? Jeśli tak, to ile poziomów? Jeśli tylko 1 zagnieżdżony poziom, co dzieje się z przecinkiem poza wewnętrzną zagnieżdżoną ofertą ale w zewnętrznym cudzysłowie? Powinieneś również wziąć pod uwagę, że pojedyncze cytaty występują same w sobie jako apostrofy (tj. tak jak w kontrprzykładzie, który dałem wcześniej z samem).Na koniec, wyrecytowane wyrażenie nie traktuje pojedynczych cudzysłowów na równi z podwójnymi cudzysłowami, ponieważ zakłada, że ​​ostatni typ cudzysłowu jest koniecznie podwójnym cudzysłowem - i zastąpienie ostatniego podwójnego cudzysłowu przez ["|"] również ma problem jeśli tekst nie pochodzi z poprawnym cytowaniem (lub jeśli używane są apostrofy), prawdopodobnie prawdopodobnie moglibyśmy założyć, że wszystkie cytaty są poprawnie zdefiniowane.

Wyrażenie regularne MarkusQ odpowiada na pytanie: znajdź wszystkie przecinki o parzystym numerze podwójnych cudzysłowów (poza podwójnymi cudzysłowami) i pomijaj wszystkie przecinki, które mają nieparzystą liczbę podwójnych cudzysłowów (tzn. są w cudzysłowach) .To jest na ogół to samo rozwiązanie, co prawdopodobnie chcesz, ale Spójrz na kilka anomalii: po pierwsze, jeśli ktoś na końcu odejdzie od cudzysłowu, to wyrażenie regularne znajdzie wszystkie błędne przecinki, zamiast znajdować pożądane lub nie pasuje do żadnego. Oczywiście, jeśli brakuje podwójnego cudzysłowu, wszystkie zakłady są wyłączone, ponieważ może nie być jasne, czy brakujący element należy na końcu, czy też należy na początku; jednak jest przypadek, który jest uzasadniony i gdzie regex mógłby zawodzić (jest to druga "anomalia"). Jeśli dostosujesz wyrażenie regularne, aby przejść przez linie tekstu, powinieneś wiedzieć, że cytowanie wielu kolejnych akapitów wymaga umieszczenia pojedynczego podwójnego cudzysłowu na początku każdego akapitu i pomijając cytat na końcu każdego akapitu, z wyjątkiem koniec ostatniego akapitu. Oznacza to, że w przestrzeni tych akapitów regex zawiedzie w niektórych miejscach i odniesie sukces w innych.

Przykłady i krótkie dyskusje na temat cytowania akapitów i zagnieżdżonych cytowań można znaleźć tutaj http://en.wikipedia.org/wiki/Quotation_mark.

+4

To nie zapewnia odpowiedzi na pytanie. Do krytyki lub wniosku wyjaśnienia od autora, zostaw komentarz poniżej ich posta – mattt

+0

Muszę spojrzeć na ten problem jeszcze raz, ale zauważyłem, że moja "odpowiedź" była dość długa.Być to pasuje jako komentarz? Odpowiedź moja stara wydaje się odpowiedzieć że nie ma jednej poprawnej odpowiedzi z powodu niejasności w pytaniu (podałem przykłady) Prawdopodobnie uznałem, że ta odpowiedź/krytyka wykracza poza uwagę autora i dodaje kontekst osobom szukającym odpowiedzi. edytuj pytanie lub czy będę musiał polegać na kimś innym? [musisz zajrzeć dalej do tego problemu, który podnosisz, gdy znajduję czas] –

+0

@mattt did nie oznacza, że ​​wydaje się, że lekceważę twoją prośbę. Teraz mam mało czasu. –

6

poniższej regexes pasowałby wszystkim przecinek, które występują poza cudzysłów,

,(?=(?:[^"]*"[^"]*")*[^"]*$) 

DEMO

LUB (PCRE tylko)

"[^"]*"(*SKIP)(*F)|, 

"[^"]*" pasuje do wszystkich podwójnie cytowany blok. Oznacza to, że na tym wejściu buz,"bar,foo" to wyrażenie pasuje tylko do "bar,foo". Teraz następujące (*SKIP)(*F) sprawia, że ​​mecz się nie powiedzie. Następnie przechodzi do wzoru znajdującego się obok symbolu | i próbuje dopasować znaki z pozostałego łańcucha. Oznacza to, że w naszym wyjściu , obok wzorca | zostanie dopasowany tylko przecinek, który był tuż po buz. Zauważ, że nie będzie to pasowało do przecinka, który był obecny w podwójnych cudzysłowach, ponieważ już pomijamy część podwójnie cytowaną.

DEMO


Poniższa regex pasowałby wszystkim przecinek, które są obecne wewnątrz cudzysłowów,

,(?!(?:[^"]*"[^"]*")*[^"]*$) 

DEMO

2

ile to możliwe, aby włamać się go z regex (i lubię nadużywać wyrażeń regularnych tak samo jak następnego faceta), wcześniej lub później będziesz miał kłopoty z obsługą podłańcuchów bez więcej zaawansowany analizator składni. Możliwe sposoby wpadania w kłopoty obejmują mieszane cudzysłowy i cytowane cudzysłów.

Ta funkcja podzieli ciąg na przecinki, ale nie na przecinki, które są w ciągu pojedynczym lub podwójnym cudzysłowem.Można go łatwo rozszerzyć o dodatkowe znaki, które można wykorzystać jako cytaty (chociaż pary znaków, takie jak "», będą wymagały kilku linii kodu), a nawet powiedzą, jeśli nie pamiętasz o zamknięciu cytatu w swoich danych:

function splitNotStrings(str){ 
    var parse=[], inString=false, escape=0, end=0 

    for(var i=0, c; c=str[i]; i++){ // looping over the characters in str 
    if(c==='\\'){ escape^=1; continue} // 1 when odd number of consecutive \ 
    if(c===','){ 
     if(!inString){ 
     parse.push(str.slice(end, i)) 
     end=i+1 
     } 
    } 
    else if(splitNotStrings.quotes.indexOf(c)>-1 && !escape){ 
     if(c===inString) inString=false 
     else if(!inString) inString=c 
    } 
    escape=0 
    } 
    // now we finished parsing, strings should be closed 
    if(inString) throw SyntaxError('expected matching '+inString) 
    if(end<i) parse.push(str.slice(end, i)) 
    return parse 
} 

splitNotStrings.quotes="'\"" // add other (symmetrical) quotes here 
Powiązane problemy