Obtenir une chaîne entre deux chaînes dans une chaîne

103

J'ai une chaîne comme:

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

Je veux juste garder la chaîne qui est entre "key : "et " - ". Comment puis je faire ça? Dois-je utiliser un Regex ou puis-je le faire d'une autre manière?

couler
la source
2
utiliser substringetindexof
Sayse
Récupère la chaîne après une chaîne particulière dans une chaîne et avant une autre chaîne spécifique qui est également contenue dans la chaîne où se trouve l'ancienne chaîne ..
Ken Kin

Réponses:

161

Peut-être qu'un bon moyen est simplement de couper une sous - chaîne :

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
la source
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 avec juste des opérations de chaîne

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

Vous pouvez le faire sans regex

 input.Split(new string[] {"key :"},StringSplitOptions.None)[1]
      .Split('-')[0]
      .Trim();
Anirudha
la source
6
Cela créerait plusieurs chaînes inutiles en mémoire. Ne l'utilisez pas si vous vous souciez de la mémoire.
Mikael Dúi Bolinder
14

En fonction de la robustesse / flexibilité de votre implémentation, cela peut en fait être un peu délicat. Voici l'implémentation que j'utilise:

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 "
ChaseMédaillon
la source
J'ai utilisé votre code, mais j'ai trouvé un petit bogue quand à @ this.IndexOf (jusqu'à, startIndex + fromLength, comparaison) à partir de chaînes comme «AB» où A est de et B est jusqu'à, donc j'ai supprimé + fromLength. Je ne l'ai pas testé profondément
Adrian Iftode
1
@AdrianIftode: bon appel. C'était définitivement un bug. Il est logique de démarrer la recherche de la deuxième ancre à startIndex, car c'est déjà passé la fin de la première ancre. J'ai corrigé le code ici.
ChaseMedallion
InvariantCulturene fonctionne pas avec les applications universelles Windows. Existe-t-il un moyen de le supprimer en conservant les fonctionnalités de votre classe? @ChaseMedallion
Leon
@Leon: vous devriez pouvoir extraire tous les éléments liés à la culture et .NET utilisera simplement la culture actuelle pour l'opération indexOf. Je ne suis pas familier avec Windows Universal Apps, donc je ne peux pas le dire avec certitude.
ChaseMedallion
13

Voici comment je peux faire ça

   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
la source
13

Je pense que cela fonctionne:

   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
la source
Excellente solution. Merci!
arcee123
10

Regex est exagéré ici.

Vous pouvez utiliser string.Splitavec la surcharge qui prend un string[]pour les délimiteurs, mais ce serait également exagéré.

Regardez Substringet IndexOf- le premier pour obtenir des parties d'une chaîne donnée et un index et une longueur et le second pour trouver indexé des chaînes / caractères internes.

Oded
la source
2
Ce n'est pas exagéré ... en fait, je dirais que Substring et IndexOf sont sous-qualifiés. Je dirais que string.Split est à peu près correct. Regex est exagéré.
C'estNOTALie.
2
Le point d'être exagéré ou sous-tué est sans objet, car la réponse répond à la demande de l'affiche de le faire d'une autre manière que Regex.
Karl Anderson
2
@newStackExchangeInstance: il échoue également s'il y a un "-" avant la "clé:". La sous-chaîne est parfaite.
jmoreno
@newStackExchangeInstance - Je crois qu'il parle string.Split.
Oded le
7

Une solution LINQ fonctionnelle:

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
la source
Cela fonctionne-t-il uniquement pour les espaces réservés à un seul caractère?
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
la source
1
Votre code entraînerait le retour des deux points au début de newString.
tsells
5

Puisque le :et le -sont uniques, vous pouvez utiliser:

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
la source
Cette réponse n'ajoute rien de significatif au nombre déjà important de réponses existantes.
Mephy
4

ou, avec une 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;

avec un exemple courant .

Vous pouvez décider si c'est exagéré.

ou

comme méthode d'extension sous-validée

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

Ceci renvoie uniquement la ou les valeurs entre "key:" et l'occurrence suivante de "-"

fboethius
la source
3

Vous pouvez utiliser la méthode d'extension ci-dessous:

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'utilisation est:

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

J'ai utilisé l'extrait de code de Vijay Singh Rana qui fait essentiellement le travail. Mais cela pose des problèmes si le firstStringcontient déjà le fichier lastString. Ce que je voulais, c'était extraire un access_token d'une réponse JSON (aucun analyseur JSON chargé). Mon firstStringétait \"access_token\": \"et mon lastStringétait \". J'ai fini avec une petite modification

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
la source
1
Il y a redondance. pos1 a été ajouté à pos2, puis soustrait de pos2.
Jfly
Merci, vous avez raison. J'ai corrigé l'exemple ci-dessus.
nvm-uli
2

Si vous recherchez une solution 1 ligne, c'est celle-ci:

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

La solution complète en 1 ligne, avec 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
la source
1

Vous avez déjà de bonnes réponses et je me rends compte que le code que je fournis est loin d'être le plus efficace et le plus propre. Cependant, j'ai pensé que cela pourrait être utile à des fins éducatives. Nous pouvons utiliser des classes et des bibliothèques pré-construites toute la journée. Mais sans comprendre le fonctionnement interne, nous imitons et répétons simplement et n'apprendrons jamais rien. Ce code fonctionne et est plus basique ou «vierge» que certains des autres:

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

Vous vous retrouvez avec la chaîne souhaitée affectée à la variable parsedString. Gardez à l'esprit qu'il capturera également les espaces précédents et précédents. N'oubliez pas qu'une chaîne est simplement un tableau de caractères qui peut être manipulé comme d'autres tableaux avec des indices, etc.

Prends soin de toi.

flyNflip
la source
C'est le meilleur algorithme bien que le pire dans la création de chaînes. Toutes les réponses fournies qui ne sont pas uniquement des regex sont faciles à déclencher pour créer des chaînes, mais celle-ci est la pire de toutes dans ce sens. Si vous aviez juste capturé le début et la fin de la chaîne à capturer et utilisé «string.Substring» pour l'extraire, ce serait parfait.
Paulo Morgado
Je suis d'accord. Comme je l'ai mentionné, c'est loin d'être efficace. Je ne recommanderais pas d'utiliser cet algorithme. Il s'agit simplement de "" baisser le son "pour qu'il puisse comprendre les cordes à un niveau inférieur. S'il veut simplement faire le travail, il avait déjà des réponses qui y parviendraient.
flyNflip
J'ai compris ça. Je voulais juste souligner ses points forts et de la semaine. Cependant, pour répondre à la question initiale, il en faut un peu plus car il doit correspondre aux limites d'une chaîne et pas seulement aux limites de caractères. Mais l'idée est la même.
Paulo Morgado
1

Si vous souhaitez gérer plusieurs occurrences de paires de sous-chaînes, ce ne sera pas facile sans RegEx:

Regex.Matches(input ?? String.Empty, "(?=key : )(.*)(?<= - )", RegexOptions.Singleline);
  • input ?? String.Empty évite une exception nulle d'argument
  • ?=conserve la 1ère sous-chaîne et ?<=conserve la 2ème sous-chaîne
  • RegexOptions.Singleline autorise les sauts de ligne entre les paires de sous-chaînes

Si l'ordre et le nombre d'occurrences des sous-chaînes n'ont pas d'importance, celui-ci rapide et sale peut être une option:

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

Au moins, il évite la plupart des exceptions, en renvoyant la chaîne d'origine si aucune / seule sous-chaîne ne correspond.

Teodor Tite
la source
0

Comme je le dis toujours, rien n'est impossible:

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

Rappelez-vous qui devrait ajouter une référence de System.Text.RegularExpressions

J'espère que j'ai aidé.

Slavi
la source
0

Quelque chose comme ça peut-être

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

Lorsque les questions sont formulées à partir d'un seul exemple, des ambiguïtés sont inévitablement présentes. Cette question ne fait pas exception.

Pour l'exemple donné dans la question, la chaîne souhaitée est claire:

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

Cependant, cette chaîne n'est qu'un exemple de chaînes et de chaînes de limite pour lesquelles certaines sous-chaînes doivent être identifiées. Je considérerai une chaîne générique avec des chaînes de limite génériques, représentées comme suit.

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

PPest la chaîne précédente , FFest la chaîne suivante et les chapeaux de partie indiquent quelles sous-chaînes doivent être mises en correspondance. (Dans l'exemple donné dans la question key : est la chaîne précédente et -est la chaîne suivante.) J'ai supposé que PPet FFsont précédés et suivis par des limites de mot (de sorte que PPAet FF8ne correspondent pas).

Mes hypothèses, telles que reflétées par les chapeaux de fête, sont les suivantes:

  • La première sous PP- chaîne peut être précédée d'une (ou plusieurs) FFsous-chaînes qui, si elles sont présentes, ne sont pas prises en compte;
  • Si PPest suivi d'un ou plusieurs PPs avant de FFrencontrer, les PPs suivants font partie de la sous-chaîne entre les chaînes précédente et suivante;
  • Si PPest suivi d'un ou de plusieurs FFs avant la PPrencontre d'un, le premier FFsuivant PPest considéré comme la chaîne suivante.

Notez que la plupart des réponses ici ne traitent que des chaînes du formulaire

abc PP def FF ghi
      ^^^^^

ou

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

On peut utiliser une expression régulière, des constructions de code ou une combinaison des deux pour identifier les sous-chaînes d'intérêt. Je ne porte aucun jugement sur la meilleure approche. Je ne présenterai que l'expression régulière suivante qui correspondra aux sous-chaînes d'intérêt.

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

Démarrez votre moteur! 1

J'ai testé cela avec le moteur de regex PCRE (PHP), mais comme le regex n'est pas du tout exotique, je suis sûr qu'il fonctionnera avec le moteur de regex .NET (qui est très robuste).

Le moteur regex effectue les opérations suivantes:

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

Cette technique, consistant à faire correspondre un caractère à la fois, en suivant la chaîne précédente, jusqu'à ce que le caractère soit Fet soit suivi par F(ou plus généralement, le caractère étant la chaîne qui constitue la chaîne suivante), est appelée Solution de jeton gourmand tempéré .

Naturellement, l'expression rationnelle devrait être modifiée (si possible) si les hypothèses que j'ai énoncées ci-dessus sont modifiées.

1. Déplacez le curseur pour des explications détaillées.

Cary Swoveland
la source
0

Dans C # 8.0 et supérieur, vous pouvez utiliser l'opérateur de plage ..comme dans

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

Consultez la documentation pour plus de détails.

user3517546
la source