Uzyskaj ciąg między dwoma ciągami w ciągu


103

Mam taki ciąg:

"super exemple of string key : text I want to keep - end of my string"

Chcę po prostu zachować ciąg znajdujący się między "key : "a " - ". Jak mogę to zrobić? Czy muszę używać Regex, czy mogę to zrobić w inny sposób?


2
użyj substringiindexof
Sayse

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 :

String St = "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);

37
string input = "super exemple of string key : text I want to keep - end of my string";
var match = Regex.Match(input, @"key : (.+?)-").Groups[1].Value;

lub tylko z operacjami na łańcuchach

var start = input.IndexOf("key : ") + 6;
var match2 = input.Substring(start, input.IndexOf("-") - start);

29

Możesz to zrobić bez wyrażenia regularnego

 input.Split(new string[] {"key :"},StringSplitOptions.None)[1]
      .Split('-')[0]
      .Trim();

6
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:

public static class StringExtensions {
    /// <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>
    public static string Substring(this string @this, string from = 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) { throw new ArgumentException("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) { throw new ArgumentException("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.
ChaseMedallion,

13

Oto sposób, w jaki mogę to zrobić

   public string Between(string STR , string FirstString, string LastString)
    {       
        string FinalString;     
        int Pos1 = STR.IndexOf(FirstString) + FirstString.Length;
        int Pos2 = STR.IndexOf(LastString);
        FinalString = STR.Substring(Pos1, Pos2 - Pos1);
        return FinalString;
    }

13

Myślę, że to działa:

   static void Main(string[] args)
    {
        String text = "One=1,Two=2,ThreeFour=34";

        Console.WriteLine(betweenStrings(text, "One=", ",")); // 1
        Console.WriteLine(betweenStrings(text, "Two=", ",")); // 2
        Console.WriteLine(betweenStrings(text, "ThreeFour=", "")); // 34

        Console.ReadKey();

    }

    public static String betweenStrings(String text, String start, String end)
    {
        int p1 = text.IndexOf(start) + start.Length;
        int p2 = text.IndexOf(end, p1);

        if (end == "") return (text.Substring(p1));
        else return text.Substring(p1, p2 - p1);                      
    }

Świetne rozwiązanie. Dzięki!
arcee123

10

Regex to przesada.

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.


2
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 = new string(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);

1
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(new char[] { ':', '-' })[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;

...

var value =
    Regex.Match(
        "super exemple of string key : text I want to keep - end of my string",
        "key : (.*) - ")
    .Groups[1].Value;

z przykładem biegania .

Możesz zdecydować, czy to przesada.

lub

jako niedostatecznie zwalidowana metoda rozszerzenia

using System.Text.RegularExpressions;

public class Test
{
    public static void Main()
    {
        var value =
                "super exemple of string key : text I want to keep - end of my string"
                    .Between(
                        "key : ",
                        " - ");

        Console.WriteLine(value);
    }
}

public static class Ext
{
    static string Between(this string source, string left, string right)
    {
        return Regex.Match(
                source,
                string.Format("{0}(.*){1}", left, right))
            .Groups[1].Value;
    }
}

4
var matches = Regex.Matches(input, @"(?<=key :)(.+?)(?=-)");

Zwraca tylko wartości między „klucz:” a następującym wystąpieniem „-”


3

Możesz skorzystać z poniższej metody rozszerzenia:

public static string GetStringBetween(this string 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 : ", " - ");

3

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

string Between(string str, string firstString, string lastString)
{    
    int pos1 = str.IndexOf(firstString) + firstString.Length;
    int pos2 = str.Substring(pos1).IndexOf(lastString);
    return str.Substring(pos1, pos2);
}

1
Jest nadmiarowość. poz1 został dodany do poz2, a następnie odjęty od poz2.
Jfly

Dzięki, masz rację. Poprawiłem powyższy przykład.
nvm-uli

2

Jeśli szukasz rozwiązania 1-liniowego, oto jest:

s.Substring(s.IndexOf("eT") + "eT".Length).Split("97".ToCharArray()).First()

Całe rozwiązanie 1-liniowe z System.Linq:

using System;
using System.Linq;

class OneLiner
{
    static void Main()
    {
        string s = "TextHereTisImortant973End"; //Between "eT" and "97"
        Console.WriteLine(s.Substring(s.IndexOf("eT") + "eT".Length)
                           .Split("97".ToCharArray()).First());
    }
}

1

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.

Dbać.


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:

Regex.Matches(input ?? String.Empty, "(?=key : )(.*)(?<= - )", RegexOptions.Singleline);
  • input ?? String.Empty unika wyjątku zerowego argumentu
  • ?=zachowuje pierwszy podciąg i ?<=zachowuje drugi podciąg
  • RegexOptions.Singleline dopuszcza znak nowej linii między parą podciągów

Jeśli kolejność i liczba wystąpień podciągów nie ma znaczenia, ten szybki i brudny może być opcją:

var parts = input?.Split(new string[] { "key : ", " - " }, StringSplitOptions.None);
string result = parts?.Length >= 3 ? result[1] : input;

Przynajmniej unika większości wyjątków, zwracając oryginalny ciąg, jeśli żaden / pojedynczy podciąg nie pasuje.


0

Jak zawsze powtarzam, nie ma rzeczy niemożliwych:

string value =  "super exemple of string key : text I want to keep - end of my string";
Regex regex = new Regex(@"(key \: (.*?) _ )");
Match match = regex.Match(value);
if (match.Success)
{
    Messagebox.Show(match.Value);
}

Pamiętaj, że powinno dodać odwołanie do System.Text.RegularExpressions

Mam nadzieję, że pomogłem.


0

Może coś takiego

private static string Between(string text, string from, string to)
{
    return text[(text.IndexOf(from)+from.Length)..text.IndexOf(to, text.IndexOf(from))];
}

0

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.

abc FF def PP ghi,PP jkl,FF mno PP pqr FF,stu FF vwx,PP yza
             ^^^^^^^^^^^^         ^^^^^  

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.

(?<=\bPP\b)(?:(?!\bFF\b).)*(?=\bFF\b)

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:

(?<=          : 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 group 0+ 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.


0

W C # 8,0 i nowszych można użyć operatora zakresu, ..tak jak w

var s = "header-THE_TARGET_STRING.7z";
var from = s.IndexOf("-") + "-".Length;
var to = s.IndexOf(".7z");
var versionString = s[from..to];  // THE_TARGET_STRING

Szczegółowe informacje można znaleźć w dokumentacji .

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.