Pobierz ciąg po określonym ciągu w ciągu i przed innym określonym ciągiem, który jest również zawarty w ciągu, w którym znajduje się poprzedni ciąg ..
Ken Kin
Odpowiedzi:
161
Być może dobrym sposobem jest po prostu wycięcie podciągu :
StringSt="super exemple of string key : text I want to keep - end of my string";int pFrom =St.IndexOf("key : ")+"key : ".Length;int pTo =St.LastIndexOf(" - ");String result =St.Substring(pFrom, pTo - pFrom);
Spowoduje to utworzenie w pamięci wielu niepotrzebnych ciągów. Nie używaj tego, jeśli zależy ci na pamięci.
Mikael Dúi Bolinder
14
W zależności od tego, jak solidna / elastyczna ma być Twoja implementacja, może to być trochę trudne. Oto implementacja, której używam:
publicstaticclassStringExtensions{/// <summary>/// takes a substring between two anchor strings (or the end of the string if that anchor is null)/// </summary>/// <param name="this">a string</param>/// <param name="from">an optional string to search after</param>/// <param name="until">an optional string to search before</param>/// <param name="comparison">an optional comparison for the search</param>/// <returns>a substring based on the search</returns>publicstaticstringSubstring(thisstring@this,stringfrom=null,string until =null,StringComparison comparison =StringComparison.InvariantCulture){var fromLength =(from??string.Empty).Length;var startIndex =!string.IsNullOrEmpty(from)?@this.IndexOf(from, comparison)+ fromLength
:0;if(startIndex < fromLength){thrownewArgumentException("from: Failed to find an instance of the first anchor");}var endIndex =!string.IsNullOrEmpty(until)?@this.IndexOf(until, startIndex, comparison):@this.Length;if(endIndex <0){thrownewArgumentException("until: Failed to find an instance of the last anchor");}var subString =@this.Substring(startIndex, endIndex - startIndex);return subString;}}// usage:var between ="a - to keep x more stuff".Substring(from:"-", until:"x");// returns " to keep "
Użyłem twojego kodu, ale znalazłem mały błąd w @ this.IndexOf (do, startIndex + fromLength, porównanie) z ciągów znaków takich jak „AB”, gdzie A jest od, a B jest do, więc usunąłem + fromLength. Jednak nie testowałem tego głęboko
Adrian Iftode,
1
@AdrianIftode: dobra rozmowa. To był zdecydowanie błąd. Sensowne jest rozpoczęcie wyszukiwania drugiej kotwicy od startIndex, ponieważ jest to już koniec pierwszej kotwicy. Poprawiłem kod tutaj.
ChaseMedallion
InvariantCulturenie działa z aplikacjami uniwersalnymi systemu Windows. Czy jest jakiś sposób, aby go usunąć, zachowując funkcjonalność swojej klasy? @ChaseMedallion
Leon
@Leon: powinieneś być w stanie wyrwać wszystkie rzeczy związane z kulturą, a .NET po prostu użyje bieżącej kultury do operacji indexOf. Nie znam jednak uniwersalnych aplikacji Windows, więc nie mogę powiedzieć na pewno.
Państwo mogli korzystać string.Splitz przeciążeniem, które odbywają się string[]na ograniczniki ale to też być przesadą.
Spójrz na Substringi IndexOf- pierwsze, aby uzyskać części podanego ciągu i indeksu oraz długość, a drugie, aby znaleźć indeksowane wewnętrzne ciągi / znaki.
To nie jest przesada ... w rzeczywistości powiedziałbym, że Substring i IndexOf są niedostateczne. Powiedziałbym, że ten sznurek. Podział ma rację. Regex to przesada.
NIE jest.
2
Punkt, w którym jest to przesada lub niedostateczna liczba, jest dyskusyjny, ponieważ odpowiedź spełnia prośbę postera, aby zrobić to w inny sposób niż Regex.
Karl Anderson
2
@newStackExchangeInstance: również kończy się niepowodzeniem, jeśli przed „key:” występuje znak „-”. Podciąg jest na miejscu.
jmoreno
@newStackExchangeInstance - wydaje mi się, że on mówi string.Split.
Oded
7
Działające rozwiązanie LINQ:
string str ="super exemple of string key : text I want to keep - end of my string";string res =newstring(str.SkipWhile(c => c !=':').Skip(1).TakeWhile(c => c !='-').ToArray()).Trim();Console.WriteLine(res);// text I want to keep
Czy to działa tylko w przypadku symboli zastępczych jednoznakowych?
beppe9000
5
string str="super exemple of string key : text I want to keep - end of my string";int startIndex = str.IndexOf("key")+"key".Length;int endIndex = str.IndexOf("-");string newString = str.Substring(startIndex, endIndex - startIndex);
Twój kod spowoduje, że dwukropek zostanie zwrócony na początku nowego ciągu.
tsells
5
Ponieważ :i -są unikalne, możesz użyć:
string input;string output;
input ="super example of string key : text I want to keep - end of my string";
output = input.Split(newchar[]{':','-'})[1];
Ta odpowiedź nie dodaje nic znaczącego do już dużej liczby istniejących odpowiedzi.
Mephy
4
lub z wyrażeniem regularnym.
using System.Text.RegularExpressions;...varvalue=Regex.Match("super exemple of string key : text I want to keep - end of my string","key : (.*) - ").Groups[1].Value;
jako niedostatecznie zwalidowana metoda rozszerzenia
using System.Text.RegularExpressions;publicclassTest{publicstaticvoidMain(){varvalue="super exemple of string key : text I want to keep - end of my string".Between("key : "," - ");Console.WriteLine(value);}}publicstaticclassExt{staticstringBetween(thisstring source,string left,string right){returnRegex.Match(
source,string.Format("{0}(.*){1}", left, right)).Groups[1].Value;}}
Możesz skorzystać z poniższej metody rozszerzenia:
publicstaticstringGetStringBetween(thisstring token,string first,string second){if(!token.Contains(first))return"";var afterFirst = token.Split(new[]{ first },StringSplitOptions.None)[1];if(!afterFirst.Contains(second))return"";var result = afterFirst.Split(new[]{ second },StringSplitOptions.None)[0];return result;}
Wykorzystanie to:
var token ="super exemple of string key : text I want to keep - end of my string";var keyValue = token.GetStringBetween("key : "," - ");
Użyłem fragmentu kodu z Vijay Singh Rana, który w zasadzie spełnia swoje zadanie. Ale powoduje problemy, jeśli firstStringplik zawiera już rozszerzenie lastString. Chciałem wyodrębnić access_token z odpowiedzi JSON (nie załadowano parsera JSON). Moje firstStringbyło \"access_token\": \"i moje lastStringbyło \". Skończyło się na niewielkiej modyfikacji
using System;
using System.Linq;classOneLiner{staticvoidMain(){string s ="TextHereTisImortant973End";//Between "eT" and "97"Console.WriteLine(s.Substring(s.IndexOf("eT")+"eT".Length).Split("97".ToCharArray()).First());}}
Masz już kilka dobrych odpowiedzi i zdaję sobie sprawę, że kod, który dostarczam, jest daleki od najbardziej wydajnego i czystego. Pomyślałem jednak, że może to być przydatne do celów edukacyjnych. Możemy używać gotowych klas i bibliotek przez cały dzień. Ale bez zrozumienia wewnętrznego działania po prostu naśladujemy i powtarzamy i nigdy się niczego nie nauczymy. Ten kod działa i jest bardziej podstawowy lub „dziewiczy” niż niektóre inne:
char startDelimiter =':';char endDelimiter ='-';Boolean collect =false;string parsedString ="";foreach(char c in originalString){if(c == startDelimiter)
collect =true;if(c == endDelimiter)
collect =false;if(collect ==true&& c != startDelimiter)
parsedString += c;}
Kończysz z żądanym ciągiem przypisanym do zmiennej parsedString. Należy pamiętać, że przechwytuje również pola poprzedzające i poprzedzające. Pamiętaj, że ciąg to po prostu tablica znaków, którymi można manipulować jak innymi tablicami z indeksami itp.
To najlepszy algorytm, chociaż najgorszy w tworzeniu ciągów. Wszystkie podane odpowiedzi, które nie są tylko wyrażeniami regularnymi, są wyzwalane w tworzeniu ciągów, ale ta jest najgorsza ze wszystkich w tym sensie. Gdybyś właśnie przechwycił początek i koniec łańcucha do przechwycenia i użył „string.Substring” do wyodrębnienia, byłoby idealnie.
Paulo Morgado
Zgadzam się. Jak wspomniałem, jest to dalekie od wydajności. Nie polecałbym używania tego algorytmu. To po prostu „dumbing it down”, aby mógł zrozumieć struny na niższym poziomie. Jeśli po prostu chce wykonać swoją pracę, miał już odpowiedzi, które to umożliwią.
flyNflip
Zrozumiałem to. Wskazałem tylko jego mocne i słabe punkty. Chociaż, aby odpowiedzieć na pierwotne pytanie, wymaga trochę więcej, ponieważ musi dopasować granice ciągów, a nie tylko granice znaków. Ale idea jest taka sama.
Paulo Morgado,
1
Jeśli chcesz obsłużyć wiele wystąpień par podciągów, nie będzie to łatwe bez wyrażenia regularnego:
stringvalue="super exemple of string key : text I want to keep - end of my string";Regex regex =newRegex(@"(key \: (.*?) _ )");Match match = regex.Match(value);if(match.Success){Messagebox.Show(match.Value);}
Pamiętaj, że powinno dodać odwołanie do System.Text.RegularExpressions
Gdy pytania są zadawane w kategoriach pojedynczego przykładu, nieuchronnie pojawiają się niejasności. To pytanie nie jest wyjątkiem.
Dla przykładu podanego w pytaniu pożądany ciąg jest jasny:
super example of string key : text I want to keep - end of my string^^^^^^^^^^^^^^^^^^^
Jednak ten ciąg jest tylko przykładem łańcuchów i ciągów granicznych, dla których mają zostać zidentyfikowane określone podciągi. Rozważę ogólny ciąg z ogólnymi łańcuchami granicznymi, przedstawionymi w następujący sposób.
PPjest poprzedzającym ciągiem , FFjest następującym ciągiem, a kapelusze imprezowe wskazują, które podciągi mają zostać dopasowane. (W przykładzie podanym w pytaniu key : jest poprzedzający ciąg i -jest to następujący ciąg.) Założyłem to PPi FFsą poprzedzone i zakończone granicami słów (tak, że PPAi FF8nie są dopasowane).
Moje założenia, które odzwierciedlają czapki imprezowe, są następujące:
Pierwszy podciąg PPmoże być poprzedzony jednym (lub więcej) FFpodciągami, które, jeśli są obecne, są pomijane;
Jeśli PPnastępuje jedno lub więcej PPs przed FFnapotkaniem, następujące PPs są częścią podłańcucha między poprzedzającym a następnym ciągiem;
Jeśli PPnastępuje jedno lub więcej FFs przed PPspotkaniem, pierwszy FFnastępujący po nim PPjest uważany za następujący ciąg.
Zwróć uwagę, że wiele odpowiedzi tutaj dotyczy tylko ciągów znaków formularza
abc PP def FF ghi
^^^^^
lub
abc PP def FF ghi PP jkl FF mno
^^^^^^^^^^
Można użyć wyrażenia regularnego, konstrukcji kodu lub kombinacji tych dwóch w celu zidentyfikowania interesujących nas podciągów. Nie oceniam, które podejście jest najlepsze. Przedstawię tylko następujące wyrażenie regularne, które będzie pasowało do interesujących nas podciągów.
Przetestowałem to z silnikiem regex PCRE (PHP), ale ponieważ wyrażenie regularne wcale nie jest egzotyczne, jestem pewien, że będzie działać z silnikiem regex .NET (który jest bardzo solidny).
Silnik wyrażeń regularnych wykonuje następujące operacje:
(?<=: begin a positive lookbehind
\bPP\b : match 'PP'): end positive lookbehind
(?:: begin a non-capture group(?!: begin a negative lookahead
\bFF\b : match 'FF'): end negative lookahead
.: match any character
): end non-capture group*: execute non-capture group0+ times
(?=: begin positive lookahead
\bFF\b : match 'FF'): end positive lookahead
Ta technika polega na dopasowywaniu jednego znaku na raz, po poprzedzającym ciągu, aż do znaku Fi następuje po nimF (lub bardziej ogólnie, znak jest łańcuchem, który tworzy następujący ciąg), nazywa się Tempered Greedy Token Solution .
Oczywiście regex musiałby zostać zmodyfikowany (jeśli to możliwe), gdyby zmienione zostały założenia, które przedstawiłem powyżej.
1. Poruszaj kursorem, aby uzyskać szczegółowe wyjaśnienia.
substring
iindexof
Odpowiedzi:
Być może dobrym sposobem jest po prostu wycięcie podciągu :
źródło
lub tylko z operacjami na łańcuchach
źródło
Możesz to zrobić bez wyrażenia regularnego
źródło
W zależności od tego, jak solidna / elastyczna ma być Twoja implementacja, może to być trochę trudne. Oto implementacja, której używam:
źródło
InvariantCulture
nie działa z aplikacjami uniwersalnymi systemu Windows. Czy jest jakiś sposób, aby go usunąć, zachowując funkcjonalność swojej klasy? @ChaseMedallionOto sposób, w jaki mogę to zrobić
źródło
Myślę, że to działa:
źródło
Regex to przesada.
Państwo mogli korzystać
string.Split
z przeciążeniem, które odbywają sięstring[]
na ograniczniki ale to też być przesadą.Spójrz na
Substring
iIndexOf
- pierwsze, aby uzyskać części podanego ciągu i indeksu oraz długość, a drugie, aby znaleźć indeksowane wewnętrzne ciągi / znaki.źródło
string.Split
.Działające rozwiązanie LINQ:
źródło
źródło
Ponieważ
:
i-
są unikalne, możesz użyć:źródło
lub z wyrażeniem regularnym.
z przykładem biegania .
Możesz zdecydować, czy to przesada.
lub
jako niedostatecznie zwalidowana metoda rozszerzenia
źródło
Zwraca tylko wartości między „klucz:” a następującym wystąpieniem „-”
źródło
Możesz skorzystać z poniższej metody rozszerzenia:
Wykorzystanie to:
źródło
Użyłem fragmentu kodu z Vijay Singh Rana, który w zasadzie spełnia swoje zadanie. Ale powoduje problemy, jeśli
firstString
plik zawiera już rozszerzenielastString
. Chciałem wyodrębnić access_token z odpowiedzi JSON (nie załadowano parsera JSON). MojefirstString
było\"access_token\": \"
i mojelastString
było\"
. Skończyło się na niewielkiej modyfikacjiźródło
Jeśli szukasz rozwiązania 1-liniowego, oto jest:
Całe rozwiązanie 1-liniowe z
System.Linq
:źródło
Masz już kilka dobrych odpowiedzi i zdaję sobie sprawę, że kod, który dostarczam, jest daleki od najbardziej wydajnego i czystego. Pomyślałem jednak, że może to być przydatne do celów edukacyjnych. Możemy używać gotowych klas i bibliotek przez cały dzień. Ale bez zrozumienia wewnętrznego działania po prostu naśladujemy i powtarzamy i nigdy się niczego nie nauczymy. Ten kod działa i jest bardziej podstawowy lub „dziewiczy” niż niektóre inne:
Kończysz z żądanym ciągiem przypisanym do zmiennej parsedString. Należy pamiętać, że przechwytuje również pola poprzedzające i poprzedzające. Pamiętaj, że ciąg to po prostu tablica znaków, którymi można manipulować jak innymi tablicami z indeksami itp.
Dbać.
źródło
Jeśli chcesz obsłużyć wiele wystąpień par podciągów, nie będzie to łatwe bez wyrażenia regularnego:
Jeśli kolejność i liczba wystąpień podciągów nie ma znaczenia, ten szybki i brudny może być opcją:
Przynajmniej unika większości wyjątków, zwracając oryginalny ciąg, jeśli żaden / pojedynczy podciąg nie pasuje.
źródło
Jak zawsze powtarzam, nie ma rzeczy niemożliwych:
Mam nadzieję, że pomogłem.
źródło
Może coś takiego
źródło
Gdy pytania są zadawane w kategoriach pojedynczego przykładu, nieuchronnie pojawiają się niejasności. To pytanie nie jest wyjątkiem.
Dla przykładu podanego w pytaniu pożądany ciąg jest jasny:
Jednak ten ciąg jest tylko przykładem łańcuchów i ciągów granicznych, dla których mają zostać zidentyfikowane określone podciągi. Rozważę ogólny ciąg z ogólnymi łańcuchami granicznymi, przedstawionymi w następujący sposób.
PP
jest poprzedzającym ciągiem ,FF
jest następującym ciągiem, a kapelusze imprezowe wskazują, które podciągi mają zostać dopasowane. (W przykładzie podanym w pytaniukey :
jest poprzedzający ciąg i-
jest to następujący ciąg.) Założyłem toPP
iFF
są poprzedzone i zakończone granicami słów (tak, żePPA
iFF8
nie są dopasowane).Moje założenia, które odzwierciedlają czapki imprezowe, są następujące:
PP
może być poprzedzony jednym (lub więcej)FF
podciągami, które, jeśli są obecne, są pomijane;PP
następuje jedno lub więcejPP
s przedFF
napotkaniem, następującePP
s są częścią podłańcucha między poprzedzającym a następnym ciągiem;PP
następuje jedno lub więcejFF
s przedPP
spotkaniem, pierwszyFF
następujący po nimPP
jest uważany za następujący ciąg.Zwróć uwagę, że wiele odpowiedzi tutaj dotyczy tylko ciągów znaków formularza
lub
Można użyć wyrażenia regularnego, konstrukcji kodu lub kombinacji tych dwóch w celu zidentyfikowania interesujących nas podciągów. Nie oceniam, które podejście jest najlepsze. Przedstawię tylko następujące wyrażenie regularne, które będzie pasowało do interesujących nas podciągów.
Uruchom silnik! 1
Przetestowałem to z silnikiem regex PCRE (PHP), ale ponieważ wyrażenie regularne wcale nie jest egzotyczne, jestem pewien, że będzie działać z silnikiem regex .NET (który jest bardzo solidny).
Silnik wyrażeń regularnych wykonuje następujące operacje:
Ta technika polega na dopasowywaniu jednego znaku na raz, po poprzedzającym ciągu, aż do znaku
F
i następuje po nimF
(lub bardziej ogólnie, znak jest łańcuchem, który tworzy następujący ciąg), nazywa się Tempered Greedy Token Solution .Oczywiście regex musiałby zostać zmodyfikowany (jeśli to możliwe), gdyby zmienione zostały założenia, które przedstawiłem powyżej.
1. Poruszaj kursorem, aby uzyskać szczegółowe wyjaśnienia.
źródło
W C # 8,0 i nowszych można użyć operatora zakresu,
..
tak jak wSzczegółowe informacje można znaleźć w dokumentacji .
źródło