2016-11-29 21 views
5

Próbuję podzielić ciąg reprezentujący XPath takich jak:Dzielenie ciąg na/kiedy nie wewnątrz []

string myPath = "/myns:Node1/myns:Node2[./myns:Node3=123456]/myns:Node4"; 

muszę podzielić na „/” (zwanego dalej „/” wykluczony z wyników , tak jak w przypadku zwykłego podziału na łańcuchy), chyba że "/" znajduje się w "[...]" (gdzie "/" nie zostanie podzielone, a także uwzględnione w wyniku).

Więc co normalny string[] result = myPath.Split("/".ToCharArray()) dostaje mnie:

result[0]: //Empty string, this is ok 
result[1]: myns:Node1 
result[2]: myns:Node2[. 
result[3]: myns:Node3=123456] 
result[4]: myns:Node4 

results[2] i result[3] powinien zasadniczo być łączone i powinienem skończyć z:

result[0]: //Empty string, this is ok 
result[1]: myns:Node1 
result[2]: myns:Node2[./myns:Node3=123456] 
result[3]: myns:Node4 

Ponieważ nie jestem bardzo biegły w regex, Próbowałem ręcznie rekombinować wyniki w nowej tablicy po podziale, ale co mnie niepokoi to, że chociaż jest to trywialne, aby działało w tym przykładzie, regex wydaje się lepszym rozwiązaniem w przypadku, gdy staje się bardziej złożony xpaths.

Dla przypomnienia, mam spojrzał na następujące pytania:
Regex split string preserving quotes
C# Regex Split - commas outside quotes
Split a string that has white spaces, unless they are enclosed within "quotes"?

Chociaż powinny one być wystarczające w pomaganiu być z moim problemem, biegnę do kilku problemy/mylące aspekty, które uniemożliwiają mi udzielenie mi pomocy.
W pierwszych 2 linkach, jako początkujący w regex, trudno mi je interpretować i uczyć się. Szukają cytatów, które wyglądają identycznie pomiędzy parą lewą i prawą, więc tłumaczenie tego na [i] wprowadza mnie w błąd, a próba i błąd nie uczą mnie niczego, a raczej po prostu frustrują mnie bardziej. Rozumiem dość proste wyrażenie regularne, ale odpowiedzi na te pytania są nieco inne niż to, co obecnie rozumiem, nawet z wyjaśnieniem w pierwszym linku.
W trzecim łączu nie będę mieć dostępu do LINQ, ponieważ kod będzie używany w starszej wersji .NET.

+0

Zgadzam się, że regex w połączonych pytań można tendencję do przytłaczać początkujących ... Lubię myśleć Jestem w połowie przyzwyczajony do regexu, kiedy muszę być, ale przyznaję, że to mi przeszkadza ... –

Odpowiedz

1

Drugi opublikowany link jest rzeczywiście idealny do Twoich potrzeb. Wszystkie potrzebne jest pewne szczypanie w celu wykrycia nawiasów zamiast apostrofami:

\/(?=(?:[^[]*\[[^\]]*])*[^]]*$) 

Zasadniczo co robi to obejmuje tylko ukośniki, które są poprzedzone lewym nawiasie kwadratowym, a następnie prawy nawias kwadratowy przed następnym ukośnikiem. Można go używać tak:

string[] matches = Regex.Split(myPath, "\\/(?=(?:[^[]*\\[[^\\]]*])*[^]]*$)") 
+0

Doskonale! Dzięki! Myląca część tego połączenia była właśnie tym, czym powinien być apostrof. Ale dzięki temu mogę porównać 2 i uczyć się na nim! –

+1

Oto link do niego w akcji https://regex101.com/r/d3Kl5O/1 –

+0

To nie zadziała, gdy w literaturze pojawi się nawias, np. '/ myns: Node1/myns: Node2 [./ myns: Node3 = 123456]/myns: Node4 [text() = 'niektóre [] nawiasy']' –

1
\/(?![^\[]*\]) 

Spróbuj this.See demo.

https://regex101.com/r/uLcWux/1

Korzystanie z @ lub \\/(?![^\\[]*\\])

P.S to tylko dla prostych xpaths nie mając nested parenthesis lub [] wewnątrz quotes

+0

nie do końca: https://regex101.com/r/uLcWux/2 – flakes

+0

@CodeStranger czy może być nawias zagnieżdżony? – vks

+0

@ płatki mogą być zagnieżdżone nawiasy .... nie było tam pytanie – vks

5

XPath jest skomplikowany język, próbując podzielić wyrażenie XPath na ukośniki na poziomie gruntu nie w wielu sytuacjach, przykłady:

/myns:Node1/myns:Node2[./myns:Node3=123456]/myns:Node4 
string(/myns:Node1/myns:Node2) 

I zasugerować inne podejście w celu objęcia większej liczby spraw. Zamiast próbować podzielić, spróbuj dopasować każdą część między ukośnikami metodą Regex.Matches(String, String). Zaletą tego sposobu jest to, że można swobodnie opisać jak wyglądają te części:

string pattern = @"(?xs) 
    [^][/()]+ # all that isn't a slash or a bracket 
    (?: # predicates (eventually nested) 
     \[ 
     (?: [^]['""] | (?<c>\[) | (?<-c>]) 
      | "" (?> [^""\\]* (?: \\. [^""\\]*)*) "" # quoted parts 
      | ' (?> [^'\\]* (?: \\. [^'\\]* )*) ' 
     )*? 
     (?(c)(?!$)) # check if brackets are balanced 
     ] 
     | # same thing for round brackets 
     \(
     (?: [^()'""] | (?<d>\() | (?<-d>\)) 
      | "" (?> [^""\\]* (?: \\. [^""\\]*)*) "" 
      | ' (?> [^'\\]* (?: \\. [^'\\]* )*) ' 
     )*? 
     (?(d)(?!$)) 
     \) 
    )* 
    | 
    (?<![^/])(?![^/]) # empty string between slashes, at the start or end 
"; 

Uwaga: aby mieć pewność, że łańcuch jest całkowicie przetwarzane, można dodać na koniec coś podobnego wzoru: |\z(?<=(.)). W ten sposób możesz sprawdzić, czy grupa przechwytująca istnieje, aby wiedzieć, czy jesteś na końcu łańcucha. (Ale można również używać położenia meczu, długość i długość łańcucha.)

demo

+0

To byłby regex, którego nie możemy tweetować: P – vks

+1

@vks: Trochę długo, przyznaję. –

+0

Przy tak długim wyglądzie wolałbym raczej sparsować ciąg ręcznie. :/ – Abion47

2

Jeśli wymagana jest regex deseń złożoności jak Kazimierz et Hippolyte sugeruje, to może Regex nie jest najlepsza opcja w tej sytuacji. Aby dodać non-Regex możliwego rozwiązania, oto co proces może wyglądać, gdy ciąg jest analizowany ręcznie XPath:

public string[] Split(string input, char splitChar, char groupStart, char groupEnd) 
{ 
    List<string> splits = new List<string>(); 

    int startIdx = 0; 
    int groupNo = 0; 

    for (int i = 0; i < input.Length; i++) 
    { 
     if (input[i] == splitChar && groupNo == 0) 
     { 
      splits.Add(input.Substring(startIdx, i - startIdx)); 
      startIdx = i + 1; 
     } 
     else if (input[i] == groupStart) 
     { 
      groupNo++; 
     } 
     else if (input[i] == groupEnd) 
     { 
      groupNo = Math.Max(groupNo - 1, 0); 
     } 
    } 

    splits.Add(input.Substring(startIdx, input.Length - startIdx)); 

    return splits.Where(s => !string.IsNullOrEmpty(s)).ToArray(); 
} 

Osobiście uważam, że jest to o wiele łatwiejsze do zrozumienia i wdrożenia zarówno. Aby go użyć, można wykonać następujące czynności:

var input = "/myns:Node1/myns:Node2[./myns:Node3=123456]/myns:Node4[text(‌​)='some[] brackets']"; 
var split = Split(input, '/', '[', ']'); 

To wyjście będzie następujący:

split[0] = "myns:Node1" 
split[1] = "myns:Node2[./myns:Node3=123456]" 
split[2] = "myns:Node4[text(‌​)='some[] brackets']" 
+0

Na marginesie, to nie dlatego, że wzór jest długi, że jest mniej wydajny niż krótszy wzór. Pamiętaj, że coś takiego jak 'a (?!. * B)' musi przetestować ciąg aż do końca dla każdego udanego meczu. Tak nie jest w moim schemacie, w którym każdy wynik jest oddzielony od poprzedniego tylko jedną wadliwą pozycją (przy każdym ukośniku). Ale przyznaję, że można lepiej napisać używając grupy przechwytywania, aby wyodrębnić części i kotwicę '\ G', aby zapewnić zgodność wyników. –

+1

@CasimiretHippolyte Nie mówię, że Regex jest niepożądany, ponieważ dłuższy wzór oznacza mniejszą skuteczność. Twierdzę, że im dłuższy staje się wzór Regex, tym trudniej jest go debugować, utrzymywać i rozumieć. Doprowadza to do tego, że zmuszasz narzędzie do pracy bez żadnego powodu poza tym, że zdecydowałeś się użyć tego narzędzia. W takich przypadkach niestandardowa metoda analizy staje się bardziej pożądana, ponieważ jest łatwiejsza do zrozumienia i obsługi, a także może być wydajniejsza w wykonywaniu. (Zawsze kieruj się zasadą KISS.) – Abion47

+0

@CasimiretHippolyte Uruchomiłem podstawowy test porównawczy, a podczas 10 000 iteracji mój parser działa 2-3 razy szybciej niż podejście Regex. https://dotnetfiddle.net/fld3p8 – Abion47