Zeichenfolge zwischen zwei Zeichenfolgen in einer Zeichenfolge abrufen


102

Ich habe eine Zeichenfolge wie:

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

Ich möchte nur die Zeichenfolge behalten, die zwischen "key : "und liegt " - ". Wie kann ich das machen? Muss ich einen Regex verwenden oder kann ich es auf andere Weise tun?


2
benutze substringundindexof
Sayse

Holen Sie sich die Zeichenfolge nach einer bestimmten Zeichenfolge in einer Zeichenfolge und vor einer anderen bestimmten Zeichenfolge, die auch in der Zeichenfolge enthalten ist, in der sich die vorherige Zeichenfolge befindet.
Ken Kin

Antworten:


160

Vielleicht ist ein guter Weg, nur einen Teilstring auszuschneiden :

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;

oder mit nur String-Operationen

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

29

Sie können es ohne Regex tun

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

5
Dies würde mehrere nicht benötigte Zeichenfolgen im Speicher erstellen. Verwenden Sie dies nicht, wenn Sie sich für das Gedächtnis interessieren.
Mikael Dúi Bolinder

14

Je nachdem, wie robust / flexibel Ihre Implementierung sein soll, kann dies tatsächlich etwas schwierig sein. Hier ist die Implementierung, die ich verwende:

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 "

Ich habe Ihren Code verwendet, aber bei @ this.IndexOf (bis, startIndex + fromLength, Vergleich) habe ich einen kleinen Fehler bei Zeichenfolgen wie „AB” gefunden, wobei A von und B bis ist, also habe ich + vonLength entfernt. Ich habe es aber nicht
gründlich

1
@AdrianIftode: Guter Anruf. Dies war definitiv ein Fehler. Es ist sinnvoll, die Suche nach dem zweiten Anker bei startIndex zu starten, da dies bereits nach dem Ende des ersten Ankers erfolgt. Ich habe den Code hier behoben.
ChaseMedallion

InvariantCulturefunktioniert nicht mit Windows Universal Apps. Gibt es eine Möglichkeit, es zu entfernen, indem die Funktionalität Ihrer Klasse beibehalten wird? @ChaseMedallion
Leon

@Leon: Sie sollten in der Lage sein, alle kulturbezogenen Inhalte herauszureißen, und .NET verwendet nur die aktuelle Kultur für die indexOf-Operation. Ich bin jedoch nicht mit Windows Universal Apps vertraut, daher kann ich nicht sicher sagen.
ChaseMedallion

13

Hier ist der Weg, wie ich das machen kann

   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

Ich denke das funktioniert:

   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);                      
    }

Tolle Lösung. Vielen Dank!
Arcee123

10

Regex ist hier übertrieben.

Sie könnten verwenden string.Splitmit der Überlastung , die eine nimmt string[]für die Trennzeichen , aber das wäre auch sein übertrieben.

Schauen Sie sich Substringund IndexOf- erstere an, um Teile einer Zeichenfolge und einen Index sowie eine Länge zu erhalten, und die zweite, um indizierte innere Zeichenfolgen / Zeichen zu finden.


2
Es ist kein Overkill ... in der Tat würde ich sagen, dass Substring und IndexOf Underkill sind. Ich würde sagen, dass string.Split ungefähr richtig ist. Regex ist übertrieben.
wahr.

2
Der Punkt, an dem es zu viel oder zu wenig ist, ist umstritten, da die Antwort die Bitte des Posters erfüllt, es anders als Regex zu tun.
Karl Anderson

2
@newStackExchangeInstance: Es schlägt auch fehl, wenn vor dem "Schlüssel:" ein "-" steht. Teilstring ist genau richtig.
jmoreno

@newStackExchangeInstance - Ich glaube, er spricht über string.Split.
Oded

7

Eine funktionierende LINQ-Lösung:

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

Funktioniert dies nur für Platzhalter mit einem Zeichen?
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
Ihr Code würde dazu führen, dass der Doppelpunkt am Anfang des neuen Strings zurückgegeben wird.
Tsells

5

Da das :und das -einzigartig sind, können Sie Folgendes verwenden:

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];

Diese Antwort fügt der bereits großen Anzahl vorhandener Antworten nichts Sinnvolles hinzu.
Mephy

4

oder mit einem regulären Ausdruck.

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;

mit einem laufenden Beispiel .

Sie können entscheiden, ob es übertrieben ist.

oder

als untervalidierte Erweiterungsmethode

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 :)(.+?)(?=-)");

Dies gibt nur die Werte zwischen "key:" und dem folgenden Vorkommen von "-" zurück.


3

Sie können die folgende Erweiterungsmethode verwenden:

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;
    }

Verwendung ist:

var token = "super exemple of string key : text I want to keep - end of my string";
var keyValue = token.GetStringBetween("key : ", " - ");

3

Ich habe das Code-Snippet von Vijay Singh Rana verwendet, das im Grunde den Job macht. Aber es verursacht Probleme, wenn das firstStringbereits das enthält lastString. Ich wollte ein access_token aus einer JSON-Antwort extrahieren (kein JSON-Parser geladen). Mein firstStringwar \"access_token\": \"und mein lastStringwar \". Am Ende hatte ich eine kleine Modifikation

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
Es gibt Redundanz. pos1 wurde zu pos2 hinzugefügt und dann von pos2 abgezogen.
Jfly

Danke, du hast recht. Ich habe das obige Beispiel korrigiert.
NVM-Uli

2

Wenn Sie nach einer 1-Zeilen-Lösung suchen, ist dies:

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

Die gesamte 1-Zeilen-Lösung mit 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

Sie haben bereits einige gute Antworten und mir ist klar, dass der von mir bereitgestellte Code bei weitem nicht der effizienteste und sauberste ist. Ich dachte jedoch, dass es für Bildungszwecke nützlich sein könnte. Wir können den ganzen Tag vorgefertigte Klassen und Bibliotheken verwenden. Aber ohne das Innenleben zu verstehen, ahmen wir einfach nach und wiederholen es und werden nie etwas lernen. Dieser Code funktioniert und ist grundlegender oder "jungfräulicher" als einige der anderen:

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;
}

Am Ende wird der Variablen parsedString die gewünschte Zeichenfolge zugewiesen. Beachten Sie, dass auch fortlaufende und vorhergehende Leerzeichen erfasst werden. Denken Sie daran, dass eine Zeichenfolge einfach ein Array von Zeichen ist, die wie andere Arrays mit Indizes usw. bearbeitet werden können.

Pass auf.


Dies ist der beste Algorithmus, obwohl der schlechteste bei der String-Erstellung. Alle Antworten, die nicht nur für Regex bestimmt sind, können Trigger erstellen, aber diese ist in diesem Sinne die schlechteste. Wenn Sie gerade den Anfang und das Ende der zu erfassenden Zeichenfolge erfasst und "string.Substring" zum Extrahieren verwendet hätten, wäre dies perfekt.
Paulo Morgado

Genau. Wie ich bereits erwähnte, ist es alles andere als effizient. Ich würde diesen Algorithmus nicht empfehlen. Es ist einfach "dumm", damit er die Saiten auf einer niedrigeren Ebene verstehen kann. Wenn er einfach nur die Arbeit erledigen will, hatte er bereits Antworten, die dies erreichen würden.
flyNflip

Ich habe das verstanden. Ich habe nur auf seine starken und wöchentlichen Punkte hingewiesen. Um die ursprüngliche Frage zu beantworten, ist jedoch etwas mehr erforderlich, da die Zeichenfolgengrenzen und nicht nur die Zeichengrenzen übereinstimmen müssen. Aber die Idee ist genauso.
Paulo Morgado

1

Wenn Sie mehrere Vorkommen von Teilzeichenfolgenpaaren behandeln möchten, ist dies ohne RegEx nicht einfach:

Regex.Matches(input ?? String.Empty, "(?=key : )(.*)(?<= - )", RegexOptions.Singleline);
  • input ?? String.Empty vermeidet Argument Null Ausnahme
  • ?=behält den 1. Teilstring und den ?<=2. Teilstring bei
  • RegexOptions.Singleline ermöglicht Zeilenumbruch zwischen Teilzeichenfolgenpaar

Wenn die Anzahl der Reihenfolge und das Auftreten von Teilzeichenfolgen keine Rolle spielt, kann diese schnelle und schmutzige Option eine Option sein:

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

Zumindest werden die meisten Ausnahmen vermieden, indem die ursprüngliche Zeichenfolge zurückgegeben wird, wenn keine / einzelne Teilzeichenfolge übereinstimmt.


0

Wie ich immer sage, ist nichts unmöglich:

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);
}

Denken Sie daran, dass ein Verweis auf System.Text.RegularExpressions hinzugefügt werden soll

Hoffe, dass ich geholfen habe.


0

So etwas vielleicht

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

0

Wenn Fragen anhand eines einzelnen Beispiels gestellt werden, sind zwangsläufig Mehrdeutigkeiten vorhanden. Diese Frage ist keine Ausnahme.

Für das in der Frage angegebene Beispiel ist die gewünschte Zeichenfolge klar:

super example of string key : text I want to keep - end of my string
                              ^^^^^^^^^^^^^^^^^^^

Diese Zeichenfolge ist jedoch nur ein Beispiel für Zeichenfolgen und Grenzzeichenfolgen, für die bestimmte Teilzeichenfolgen identifiziert werden sollen. Ich werde eine generische Zeichenfolge mit generischen Grenzzeichenfolgen betrachten, die wie folgt dargestellt werden.

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

PPist die vorhergehende Zeichenfolge , FFist die folgende Zeichenfolge und die Partyhüte geben an, welche Teilzeichenfolgen abgeglichen werden sollen. (In dem in der Frage angegebenen Beispiel key : handelt es sich um die vorhergehende Zeichenfolge und -die folgende Zeichenfolge.) Ich habe angenommen, dass PPund FFvor und nach Wortgrenzen (so dass PPAund FF8nicht übereinstimmen).

Meine Annahmen, die sich in den Partyhüten widerspiegeln, lauten wie folgt:

  • Dem ersten Teilstring PPkann ein (oder mehrere) vorangestellt sein.FF Teilstrings , die, falls vorhanden, nicht berücksichtigt werden.
  • Wenn PPein oder mehrere PPs folgen, bevor FFFolgendes auftrittPP s Teil der Teilzeichenfolge zwischen den vorhergehenden und folgenden Zeichenfolgen.
  • Wenn vor einer Begegnung PPein oder mehrere FFs folgen PP, wird die erste FFFolge PPals die folgende Zeichenfolge betrachtet.

Beachten Sie, dass viele der Antworten hier nur Zeichenfolgen des Formulars betreffen

abc PP def FF ghi
      ^^^^^

oder

abc PP def FF ghi PP jkl FF mno
      ^^^^^         ^^^^^

Man kann einen regulären Ausdruck, Codekonstrukte oder eine Kombination der beiden verwenden, um die interessierenden Teilzeichenfolgen zu identifizieren. Ich kann nicht beurteilen, welcher Ansatz am besten ist. Ich werde nur den folgenden regulären Ausdruck präsentieren, der zu den interessierenden Teilzeichenfolgen passt.

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

Starten Sie Ihren Motor! 1

Ich habe dies mit der Regex-Engine PCRE (PHP) getestet, aber da die Regex überhaupt nicht exotisch ist, bin ich sicher, dass sie mit der Regex-Engine .NET (die sehr robust ist) funktioniert.

Die Regex-Engine führt die folgenden Vorgänge aus:

(?<=          : 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

Diese Technik besteht darin, jeweils ein Zeichen nach der vorhergehenden Zeichenfolge abzugleichen, bis das Zeichen folgt Fund folgtF (oder allgemeiner gesagt, das Zeichen ist die Zeichenfolge, aus der die folgende Zeichenfolge besteht), wird als Tempered Greedy Token Solution bezeichnet .

Natürlich müsste der reguläre Ausdruck (wenn möglich) geändert werden, wenn die oben dargelegten Annahmen geändert werden.

1. Bewegen Sie den Cursor, um detaillierte Erklärungen zu erhalten.


0

In C # 8.0 und höher können Sie den Bereichsoperator ..wie in verwenden

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

Einzelheiten finden Sie in der Dokumentation .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.