Obtener cadena entre dos cadenas en una cadena

103

Tengo una cadena como:

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

Solo quiero mantener la cadena que está entre "key : "y " - ". ¿Cómo puedo hacer eso? ¿Debo usar una expresión regular o puedo hacerlo de otra manera?

fluir
fuente
2
uso substringyindexof
Sayse
Obtenga la cadena después de una cadena en particular en una cadena y antes de otra cadena específica que también está contenida en la cadena donde está la cadena anterior ..
Ken Kin

Respuestas:

161

Quizás, una buena forma es simplemente cortar una subcadena :

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);
Dmitry Bychenko
fuente
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 operaciones de cadena

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

Puedes hacerlo sin regex

 input.Split(new string[] {"key :"},StringSplitOptions.None)[1]
      .Split('-')[0]
      .Trim();
Anirudha
fuente
6
Esto crearía múltiples cadenas innecesarias en la memoria. No use esto si le importa la memoria.
Mikael Dúi Bolinder
14

Dependiendo de cuán robusto / flexible desee que sea su implementación, esto puede ser un poco complicado. Aquí está la implementación que uso:

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 "
ChaseMedallón
fuente
Usé su código, pero encontré un pequeño error en @ this.IndexOf (hasta, startIndex + fromLength, comparación) de cadenas como "AB" donde A es de y B es hasta, así que eliminé + fromLength. Sin embargo
Adrian Iftode
1
@AdrianIftode: buena decisión. Definitivamente fue un error. Tiene sentido comenzar la búsqueda del segundo ancla en startIndex, ya que ya pasó el final del primer ancla. He arreglado el código aquí.
ChaseMedallion
InvariantCultureno funciona con aplicaciones universales de Windows. ¿Hay alguna forma de eliminarlo manteniendo la funcionalidad de su clase? @ChaseMedallion
Leon
@Leon: debería poder eliminar todas las cosas relacionadas con la cultura y .NET solo usará la cultura actual para la operación indexOf. Sin embargo, no estoy familiarizado con las aplicaciones universales de Windows, así que no puedo asegurarlo.
ChaseMedallion
13

Esta es la forma en que puedo hacer eso

   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;
    }
Vijay Singh Rana
fuente
13

Creo que esto funciona:

   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);                      
    }
fr0ga
fuente
Gran solucion ¡Gracias!
arcee123
10

Regex es exagerado aquí.

Se podría usar string.Splitcon la sobrecarga que toma un string[]para los delimitadores, pero eso también sería excesivo.

Mire Substringy IndexOf: el primero para obtener partes de una cadena dadas y un índice y una longitud y el segundo para encontrar cadenas / caracteres indexados.

Oded
fuente
2
No es exagerado ... de hecho, yo diría que Substring e IndexOf son insuficientes. Yo diría que esa cadena. Split está bien. Regex es exagerado.
It'sNotALie.
2
El punto de que sea excesivo o insuficiente es discutible, porque la respuesta cumple con la solicitud del cartel de hacerlo de otra manera que Regex.
Karl Anderson
2
@newStackExchangeInstance: también falla si hay un "-" antes de la "clave:". La subcadena es acertada.
jmoreno
@newStackExchangeInstance - Creo que está hablando string.Split.
Oded el
7

Una solución LINQ funcional:

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
wb
fuente
¿Funciona esto solo para marcadores de posición de un solo carácter?
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);
Dejan Ciev
fuente
1
Su código daría como resultado que los dos puntos se devuelvan al comienzo de newString.
tsells
5

Dado que el :y el -son únicos, puede usar:

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];
Michael Freeman
fuente
Esta respuesta no agrega nada significativo a la ya gran cantidad de respuestas existentes.
Mephy
4

o, con una expresión regular.

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 ejemplo corriente .

Puedes decidir si es exagerado.

o

como un método de extensión poco validado

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;
    }
}
Jodrell
fuente
4
var matches = Regex.Matches(input, @"(?<=key :)(.+?)(?=-)");

Esto devuelve solo el (los) valor (es) entre "clave:" y la siguiente aparición de "-"

fboecio
fuente
3

Puede utilizar el método de extensión a continuación:

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

El uso es:

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

Usé el fragmento de código de Vijay Singh Rana que básicamente hace el trabajo. Pero causa problemas si firstStringya contiene el lastString. Lo que quería era extraer un access_token de una respuesta JSON (sin analizador JSON cargado). Mi firstStringera \"access_token\": \"y mi lastStringera \". Terminé con una pequeña modificación

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);
}
nvm-uli
fuente
1
Hay redundancia. pos1 se agregó a pos2 y luego se restó de pos2.
Jfly
Gracias, tienes razón. Corregí el ejemplo anterior.
nvm-uli
2

Si está buscando una solución de 1 línea, esta es:

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

La solución completa de 1 línea, 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());
    }
}
Vityata
fuente
1

Ya tiene algunas buenas respuestas y me doy cuenta de que el código que estoy proporcionando está lejos de ser el más eficiente y limpio. Sin embargo, pensé que podría ser útil con fines educativos. Podemos usar clases y bibliotecas preconstruidas durante todo el día. Pero sin comprender el funcionamiento interno, simplemente estamos imitando y repitiendo y nunca aprenderemos nada. Este código funciona y es más básico o "virgen" que algunos de los otros:

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

Termina con la cadena deseada asignada a la variable parsedString. Tenga en cuenta que también capturará los espacios anteriores y posteriores. Recuerde que una cadena es simplemente una matriz de caracteres que se puede manipular como otras matrices con índices, etc.

Cuídate.

flyNflip
fuente
Este es el mejor algoritmo, aunque el peor en la creación de cadenas. Todas las respuestas proporcionadas que no son solo expresiones regulares son fáciles de disparar para crear cadenas, pero esta es la peor de todas en ese sentido. Si acaba de capturar el principio y el final de la cadena para capturar y usar "cadena.Substring" para extraerla, sería perfecto.
Paulo Morgado
Estoy de acuerdo. Como mencioné, está lejos de ser eficiente. No recomendaría usar este algoritmo. Es simplemente "simplificarlo" para que pueda entender las cadenas en un nivel más bajo. Si simplemente quiere hacer el trabajo, ya tenía respuestas que lo lograrían.
flyNflip
Lo entendí. Solo estaba señalando sus puntos fuertes y semanales. Aunque, para responder a la pregunta original, se requiere un poco más, ya que debe coincidir con los límites de una cadena y no solo con los límites de los caracteres. Pero la idea es la misma.
Paulo Morgado
1

Si desea manejar múltiples apariciones de pares de subcadenas, no será fácil sin RegEx:

Regex.Matches(input ?? String.Empty, "(?=key : )(.*)(?<= - )", RegexOptions.Singleline);
  • input ?? String.Empty evita la excepción de argumento nulo
  • ?=mantiene la primera subcadena y ?<=mantiene la segunda subcadena
  • RegexOptions.Singleline permite nueva línea entre pares de subcadenas

Si el orden y el recuento de ocurrencias de las subcadenas no importa, este rápido y sucio puede ser una opción:

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

Al menos evita la mayoría de las excepciones, devolviendo la cadena original si ninguna o una subcadena coinciden.

Teodor Tite
fuente
0

Como siempre digo, nada es imposible:

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

Recuerde que debería agregar una referencia de System.Text.RegularExpressions

Espero haber ayudado.

Slavi
fuente
0

Algo como esto tal vez

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

Cuando las preguntas se formulan en términos de un solo ejemplo, es inevitable que surjan ambigüedades. Esta pregunta no es una excepción.

Para el ejemplo dado en la pregunta, la cadena deseada es clara:

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

Sin embargo, esta cadena no es más que un ejemplo de cadenas y cadenas de límites para las que se deben identificar ciertas subcadenas. Consideraré una cadena genérica con cadenas de límite genéricas, representadas de la siguiente manera.

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

PPes la cadena anterior , FFes la cadena siguiente y los sombreros de fiesta indican qué subcadenas deben coincidir. (En el ejemplo dado en la pregunta key : es la cadena anterior y -es la cadena siguiente.) He asumido que PPy FFestán precedidos y seguidos por límites de palabras (de modo que PPAy FF8no coinciden).

Mis suposiciones, reflejadas en los sombreros de fiesta, son las siguientes:

  • La primera subcadena PPpuede estar precedida por una (o más) FFsubcadenas, que, si están presentes, se ignoran;
  • Si PPva seguido de uno o más PPs antes FF, los siguientes PPs son parte de la subcadena entre las cadenas anterior y siguiente;
  • Si PPva seguido de uno o más FFs antes de encontrar un PPes, el primer FFsiguiente PPse considera que es la siguiente cadena.

Tenga en cuenta que muchas de las respuestas aquí tratan solo con cadenas de la forma

abc PP def FF ghi
      ^^^^^

o

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

Se puede usar una expresión regular, construcciones de código o una combinación de las dos para identificar las subcadenas de interés. No hago ningún juicio sobre cuál es el mejor enfoque. Solo presentaré la siguiente expresión regular que coincidirá con las subcadenas de interés.

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

¡Enciende tu motor! 1

Probé esto con el motor de expresiones regulares PCRE (PHP), pero como la expresión regular no es nada exótica, estoy seguro de que funcionará con el motor de expresiones regulares .NET (que es muy robusto).

El motor de expresiones regulares realiza las siguientes operaciones:

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

Esta técnica, de hacer coincidir un carácter a la vez, siguiendo la cadena anterior, hasta que el carácter es Fy es seguido por F(o más generalmente, el carácter inicia la cadena que constituye la siguiente cadena), se llama Solución de token codicioso templado .

Naturalmente, la expresión regular tendría que modificarse (si es posible) si se cambian las suposiciones que establecí anteriormente.

1. Mueva el cursor para obtener explicaciones detalladas.

Cary Swoveland
fuente
0

En C # 8.0 y superior, puede usar el operador de rango ..como en

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

Consulte la documentación para obtener más detalles.

usuario3517546
fuente