Obter string entre duas strings em uma string

103

Eu tenho uma string como:

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

Eu quero apenas manter a corda que está entre "key : "e " - ". Como eu posso fazer isso? Devo usar um Regex ou posso fazer de outra maneira?

fluxo
fonte
2
usar substringeindexof
Sayse
Pegue a string após uma determinada string em uma string e antes de outra string específica que também está contida na string onde a string anterior está.
Ken Kin

Respostas:

161

Talvez, uma boa maneira seja apenas cortar uma substring :

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

ou apenas com operações de string

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

Você pode fazer isso sem regex

 input.Split(new string[] {"key :"},StringSplitOptions.None)[1]
      .Split('-')[0]
      .Trim();
Anirudha
fonte
6
Isso criaria várias strings desnecessárias na memória. Não use isso se você se preocupa com a memória.
Mikael Dúi Bolinder
14

Dependendo de quão robusta / flexível você deseja que sua implementação seja, isso pode ser um pouco complicado. Esta é a implementação 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 "
ChaseMedallion
fonte
Usei seu código, mas encontrei um pequeno bug quando em @ this.IndexOf (until, startIndex + fromLength, comparação) de strings como „AB” onde A é de e B é até, então removi + deLength. Eu não testei profundamente
Adrian Iftode
1
@AdrianIftode: boa chamada. Este era definitivamente um bug. Faz sentido iniciar a busca pela segunda âncora em startIndex, uma vez que já passou do final da primeira âncora. Corrigi o código aqui.
ChaseMedallion
InvariantCulturenão está funcionando com o Windows Universal Apps. Existe alguma maneira de removê-lo mantendo a funcionalidade de sua classe? @ChaseMedallion
Leon,
@Leon: você deve ser capaz de remover todas as coisas relacionadas à cultura e o .NET apenas usará a cultura atual para a operação indexOf. Não estou familiarizado com o Windows Universal Apps, portanto, não posso dizer com certeza.
ChaseMedallion de
13

Aqui está a maneira como eu posso fazer isso

   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
fonte
13

Eu acho que isso 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
fonte
Ótima solução. Obrigado!
arcee123
10

Regex é um exagero aqui.

Você poderia usar string.Splitcom a sobrecarga que exige um string[]para os delimitadores, mas isso também seria um exagero.

Olhe para Substringe IndexOf- o primeiro para obter partes de uma string fornecida e indexe um comprimento e o segundo para localizar strings / caracteres internos indexados.

Oded
fonte
2
Não é exagero ... na verdade, eu diria que Substring e IndexOf são underkill. Eu diria que string.Split está quase certo. Regex é um exagero.
It'sNotALie.
2
O ponto de ser um exagero ou subestimação é discutível, porque a resposta atende ao pedido do autor de fazer isso de uma maneira diferente do Regex.
Karl Anderson
2
@newStackExchangeInstance: também falha se houver um "-" antes da "chave:". A substring está correta.
jmoreno
@newStackExchangeInstance - Acho que ele está falando string.Split.
Oded de
7

Uma solução 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
fonte
Isso funciona apenas para marcadores de posição de um caractere?
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
fonte
1
Seu código resultaria no retorno de dois pontos no início de newString.
tsells
5

Como o :e o -são exclusivos, você pode 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
fonte
Essa resposta não adiciona nada significativo à já grande quantidade de respostas existentes.
Mephy,
4

ou, com um 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;

com um exemplo em execução .

Você pode decidir se é um exagero.

ou

como um método de extensão 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
fonte
4
var matches = Regex.Matches(input, @"(?<=key :)(.+?)(?=-)");

Isso retorna apenas o (s) valor (es) entre "chave:" e a seguinte ocorrência de "-"

fboécio
fonte
3

Você pode usar o método de extensão abaixo:

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

O uso é:

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

Usei o trecho de código de Vijay Singh Rana que basicamente faz o trabalho. Mas causará problemas se o firstStringjá contiver o lastString. O que eu queria era extrair um access_token de uma resposta JSON (sem JSON Parser carregado). Meu firstStringera \"access_token\": \"e meu lastStringera \". Acabei com uma pequena modificação

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
fonte
1
Existe redundância. pos1 foi adicionado a pos2 e, em seguida, subtraído de pos2.
Jfly
Obrigado, você está certo. Corrigi o exemplo acima.
nvm-uli
2

Se você está procurando uma solução de 1 linha, é isso:

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

A solução completa de 1 linha, com 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
fonte
1

Você já tem algumas respostas boas e percebo que o código que estou fornecendo está longe de ser o mais eficiente e limpo. No entanto, pensei que poderia ser útil para fins educacionais. Podemos usar classes e bibliotecas pré-construídas o dia todo. Mas, sem entender o funcionamento interno, estamos simplesmente imitando e repetindo e nunca aprenderemos nada. Este código funciona e é mais básico ou "virgem" do que alguns dos outros:

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

Você acaba com a string desejada atribuída à variável parsedString. Lembre-se de que ele também capturará espaços anteriores e anteriores. Lembre-se de que uma string é simplesmente uma matriz de caracteres que pode ser manipulada como outras matrizes com índices etc.

Cuidar.

flyNflip
fonte
Este é o melhor algoritmo, embora seja o pior na criação de strings. Todas as respostas fornecidas que não são apenas regex são acionadas pelo gatilho na criação de strings, mas esta é a pior de todas nesse sentido. Se você tivesse apenas capturado o início e o final da string para capturar e usado '' string.Substring '' para extraí-la, seria perfeito.
Paulo Morgado
Concordo. Como mencionei, está longe de ser eficiente. Eu não recomendaria usar este algoritmo. É simplesmente "emburrecer" para que ele possa entender strings em um nível inferior. Se ele simplesmente quiser fazer o trabalho, ele já tinha respostas que o alcançariam.
flyNflip
Eu entendi isso. Eu estava apenas apontando seus pontos fortes e fracos. Embora, para responder à pergunta original, seja necessário um pouco mais, pois precisa corresponder aos limites de uma string e não apenas aos limites dos caracteres. Mas a ideia é a mesma.
Paulo Morgado
1

Se você deseja lidar com várias ocorrências de pares de substring, não será fácil sem RegEx:

Regex.Matches(input ?? String.Empty, "(?=key : )(.*)(?<= - )", RegexOptions.Singleline);
  • input ?? String.Empty evita argumento de exceção nula
  • ?=mantém a 1ª substring e ?<=mantém a 2ª substring
  • RegexOptions.Singleline permite nova linha entre par de substring

Se a ordem e a contagem de ocorrências de substrings não importam, esta rápida e suja pode ser uma opção:

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

Pelo menos evita a maioria das exceções, retornando a string original se nenhuma / única substring corresponder.

Teodor Tite
fonte
0

Como sempre digo, nada é impossível:

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

Lembre-se de que deve adicionar referência de System.Text.RegularExpressions

Espero ter ajudado.

Eslavo
fonte
0

Algo assim talvez

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

Quando as perguntas são formuladas em termos de um único exemplo, as ambigüidades estão inevitavelmente presentes. Esta questão não é exceção.

Para o exemplo dado na pergunta, a string desejada é clara:

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

No entanto, essa string é apenas um exemplo de strings e strings de limite para as quais certas substrings devem ser identificadas. Vou considerar uma string genérica com strings de fronteira genéricas, representadas da seguinte maneira.

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

PPé a string anterior , FFé a seguinte e os chapéus de festa indicam quais substrings devem ser combinadas. (No exemplo dado na pergunta key : é a string anterior e -é a seguinte.) Presumi que PPe FFsão precedidos e seguidos por limites de palavras (de modo que PPAe FF8não são correspondidos).

Minhas suposições, conforme refletidas pelos chapéus de festa, são as seguintes:

  • A primeira substring PPpode ser precedida por uma (ou mais) FFsubstrings, que, se presentes, são desconsideradas;
  • Se PPfor seguido por um ou mais PPs antes de FFser encontrado, os seguintes PPs são parte da substring entre as strings anteriores e seguintes;
  • Se PPfor seguido por um ou mais FFs antes de um PPencontro, o primeiro FFseguinte PPé considerado a seguinte string.

Observe que muitas das respostas aqui tratam apenas de strings da forma

abc PP def FF ghi
      ^^^^^

ou

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

Pode-se usar uma expressão regular, construções de código ou uma combinação dos dois para identificar as substrings de interesse. Não faço nenhum julgamento sobre qual abordagem é a melhor. Apresentarei apenas a seguinte expressão regular que corresponderá às substrings de interesse.

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

Ligue seu motor! 1

Eu testei isso com o mecanismo regex PCRE (PHP), mas como o regex não é exótico, tenho certeza de que funcionará com o mecanismo regex .NET (que é muito robusto).

O mecanismo regex executa as seguintes operações:

(?<=          : 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 casar um caractere por vez, seguindo a string precedente, até que o caractere seja Fe seja seguido por F(ou mais geralmente, o caractere crie a string que constitui a string seguinte), é chamada de Solução de Token Guloso Temperado .

Naturalmente, a regex teria que ser modificada (se possível) se as suposições que estabeleci acima forem alteradas.

1. Mova o cursor para obter explicações detalhadas.

Cary Swoveland
fonte
0

Em C # 8.0 e acima, você pode usar o operador de intervalo ..como em

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 a documentação para obter detalhes.

usuário3517546
fonte