Ottieni una stringa tra due stringhe in una stringa


103

Ho una stringa come:

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

Voglio solo mantenere la stringa che si trova tra "key : "e " - ". Come lo posso fare? Devo usare una Regex o posso farlo in un altro modo?


2
utilizzare substringeindexof
Sayse

Prendi la stringa dopo una particolare stringa in una stringa e prima di un'altra stringa specifica che è anche contenuta nella stringa in cui si trova la precedente stringa ..
Ken Kin

Risposte:


161

Forse, un buon modo è semplicemente tagliare una sottostringa :

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;

o solo con operazioni sulle stringhe

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

29

Puoi farlo senza regex

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

6
Ciò creerebbe più stringhe non necessarie in memoria. Non usarlo se ti interessa la memoria.
Mikael Dúi Bolinder

14

A seconda di quanto robusta / flessibile vuoi che sia la tua implementazione, questo può effettivamente essere un po 'complicato. Ecco l'implementazione che utilizzo:

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 "

Ho usato il tuo codice, ma ho trovato un piccolo bug quando in @ this.IndexOf (fino a, startIndex + fromLength, confronto) da stringhe come "AB" da cui A è e B è until, quindi ho rimosso + fromLength. Non l'ho provato a fondo, però
Adrian Iftode il

1
@AdrianIftode: buona chiamata. Questo era decisamente un bug. Ha senso iniziare la ricerca del secondo ancoraggio su startIndex, poiché è già oltre la fine del primo ancoraggio. Ho corretto il codice qui.
ChaseMedallion

InvariantCulturenon funziona con le app universali di Windows. C'è un modo per rimuoverlo mantenendo la funzionalità della tua classe? @ChaseMedallion
Leon,

@Leon: dovresti essere in grado di estrarre tutte le cose relative alla cultura e .NET userà solo la cultura corrente per l'operazione indexOf. Non ho familiarità con le app universali di Windows, quindi non posso dirlo con certezza.
ChaseMedallion

13

Ecco come posso farlo

   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

Penso che funzioni:

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

Ottima soluzione. Grazie!
arcee123

10

Regex è eccessivo qui.

Si potrebbe utilizzare string.Splitcon il sovraccarico che prende una string[]per i delimitatori ma che sarebbe anche essere eccessivo.

Guarda Substringe IndexOf- il primo per ottenere parti di una stringa data e indice e una lunghezza e il secondo per trovare indicizzati stringhe / caratteri interni.


2
Non è eccessivo ... in effetti direi che Substring e IndexOf sono underkill. Direi che la stringa Spaccare è giusto. Regex è eccessivo.
ÈNotALie.

2
Il punto di essere eccessivo o insufficiente è discutibile, perché la risposta soddisfa la richiesta del poster di farlo in un modo diverso da Regex.
Karl Anderson

2
@newStackExchangeInstance: fallisce anche se c'è un "-" prima della "chiave:". La sottostringa è perfetta.
jmoreno

@newStackExchangeInstance - Credo che stia parlando string.Split.
Oded

7

Una soluzione LINQ funzionante:

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

Funziona solo per segnaposto a carattere singolo?
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
Il tuo codice comporterebbe la restituzione dei due punti all'inizio di newString.
Tsells il

5

Poiché i :e -sono unici, potresti usare:

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

Questa risposta non aggiunge nulla di significativo alla già grande quantità di risposte esistenti.
Mephy

4

o, con una regex.

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;

con un esempio in esecuzione .

Puoi decidere se è eccessivo.

o

come metodo di estensione sottovalutato

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

Restituisce solo i valori tra "key:" e la seguente occorrenza di "-"


3

Puoi utilizzare il metodo di estensione di seguito:

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

L'utilizzo è:

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

3

Ho usato lo snippet di codice di Vijay Singh Rana che fondamentalmente fa il lavoro. Ma causa problemi se il file firstStringcontiene già il file lastString. Quello che volevo era estrarre un access_token da una risposta JSON (nessun Parser JSON caricato). Il mio firstStringera \"access_token\": \"e il mio lastStringera \". Ho finito con una piccola modifica

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
C'è ridondanza. pos1 è stato aggiunto a pos2 e quindi sottratto da pos2.
Jfly

Grazie, hai ragione. Ho corretto l'esempio sopra.
nvm-uli

2

Se stai cercando una soluzione a 1 linea, questa è:

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

L'intera soluzione a 1 linea, con 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

Hai già alcune buone risposte e mi rendo conto che il codice che sto fornendo è tutt'altro che il più efficiente e pulito. Tuttavia, ho pensato che potrebbe essere utile per scopi educativi. Possiamo usare classi e librerie predefinite tutto il giorno. Ma senza comprendere il funzionamento interno, stiamo semplicemente imitando e ripetendo e non impareremo mai nulla. Questo codice funziona ed è più semplice o "vergine" di alcuni degli altri:

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

Si finisce con la stringa desiderata assegnata alla variabile parsedString. Tieni presente che acquisirà anche gli spazi precedenti e precedenti. Ricorda che una stringa è semplicemente un array di caratteri che può essere manipolato come altri array con indici ecc.

Stai attento.


Questo è il miglior algoritmo sebbene il peggiore nella creazione di stringhe. Tutte le risposte fornite che non sono solo regex sono felici di creare stringhe, ma questa è la peggiore di tutte in questo senso. Se avessi appena catturato l'inizio e la fine della stringa da catturare e avessi usato '' string.Substring '' per estrarla, sarebbe perfetto.
Paulo Morgado

Sono d'accordo. Come ho già detto, è tutt'altro che efficiente. Non consiglierei di utilizzare questo algoritmo. È semplicemente "" smorzato "in modo che possa capire le stringhe a un livello inferiore. Se vuole semplicemente portare a termine il lavoro, aveva già risposte che lo avrebbero raggiunto.
flyNflip

Io ho capito quello. Stavo solo sottolineando i suoi punti forti e settimanali. Anche se, per rispondere alla domanda originale, richiede un po 'di più in quanto deve corrispondere ai limiti di una stringa e non solo ai confini dei caratteri. Ma l'idea è la stessa.
Paulo Morgado

1

Se vuoi gestire più occorrenze di coppie di sottostringhe, non sarà facile senza RegEx:

Regex.Matches(input ?? String.Empty, "(?=key : )(.*)(?<= - )", RegexOptions.Singleline);
  • input ?? String.Empty evita l'eccezione nulla dell'argomento
  • ?=mantiene la prima sottostringa e la ?<=seconda sottostringa
  • RegexOptions.Singleline consente la nuova riga tra la coppia di sottostringhe

Se il conteggio degli ordini e delle occorrenze delle sottostringhe non è importante, questo veloce e sporco potrebbe essere un'opzione:

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

Almeno evita la maggior parte delle eccezioni, restituendo la stringa originale se nessuna / singola sottostringa corrisponde.


0

Come dico sempre niente è impossibile:

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

Ricorda che dovrebbe aggiungere il riferimento a System.Text.RegularExpressions

Spero di aver aiutato.


0

Qualcosa di simile forse

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

0

Quando le domande sono espresse in termini di un unico esempio, inevitabilmente sono presenti ambiguità. Questa domanda non fa eccezione.

Per l'esempio fornito nella domanda la stringa desiderata è chiara:

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

Tuttavia, questa stringa non è che un esempio di stringhe e stringhe limite per le quali devono essere identificate determinate sottostringhe. Prenderò in considerazione una stringa generica con stringhe di confine generiche, rappresentate come segue.

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

PPè la stringa precedente , FFè la stringa seguente e i cappelli da festa indicano quali sottostringhe devono essere abbinate. (Nell'esempio fornito nella domanda key : è la stringa precedente ed -è la stringa seguente.) Ho assunto che PPe FFsiano preceduti e seguiti da confini di parola (in modo che PPAe FF8non siano trovati).

Le mie ipotesi, come riflettono i cappelli da festa, sono le seguenti:

  • La prima sottostringa PPpuò essere preceduta da una (o più) FFsottostringhe, che, se presenti, vengono ignorate;
  • Se PPè seguito da uno o più messaggi PPprima, FFviene rilevato quanto seguePP fanno parte della sottostringa tra la stringa precedente e quella successiva;
  • Se PPè seguito da uno o più FFs prima che un PPsia incontrato, il primo che FFsegue PPè considerato la stringa seguente.

Notare che molte delle risposte qui trattano solo le stringhe del modulo

abc PP def FF ghi
      ^^^^^

o

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

Si può usare un'espressione regolare, costrutti di codice o una combinazione dei due per identificare le sottostringhe di interesse. Non giudico l'approccio migliore. Presenterò solo la seguente espressione regolare che corrisponderà alle sottostringhe di interesse.

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

Avvia il tuo motore! 1

L'ho testato con il motore regex PCRE (PHP), ma poiché la regex non è affatto esotica, sono sicuro che funzionerà con il motore regex .NET (che è molto robusto).

Il motore regex esegue le seguenti operazioni:

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

Questa tecnica, di abbinare un carattere alla volta, seguendo la stringa precedente, finché il carattere non è Fed è seguito da F(o più in generale, il carattere inizia la stringa che costituisce la stringa successiva), è chiamata Tempered Greedy Token Solution .

Naturalmente, la regex dovrebbe essere modificata (se possibile) se le ipotesi che ho esposto sopra vengono modificate.

1. Spostare il cursore per ottenere spiegazioni dettagliate.


0

In C # 8.0 e versioni successive è possibile utilizzare l'operatore di intervallo ..come in

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

Vedere la documentazione per i dettagli.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.