102 votes

Obtenir l'index de la nième occurrence d'une chaîne de caractères ?

À moins que je ne manque une méthode intégrée évidente, quel est le moyen le plus rapide d'obtenir l'adresse de l'ordinateur de l'utilisateur ? n e occurrence d'une chaîne dans une chaîne ?

Je réalise que je pourrais boucler le IndexOf en mettant à jour son indice de départ à chaque itération de la boucle. Mais le faire de cette façon me semble être un gaspillage.

0 votes

J'utiliserais une expression régulière pour cela, puis il faut trouver un moyen optimal de faire correspondre la chaîne dans la chaîne. C'est l'un des beaux DSL que nous devrions tous utiliser lorsque c'est possible. Un exemple en VB.net, le code est presque le même qu'en C#.

2 votes

Je parierais sur le fait que la version des expressions régulières est beaucoup plus difficile à mettre en place que "continuer à faire des boucles et à faire de simples String.IndexOf". Les expressions régulières ont leur place, mais ne devraient pas être utilisées lorsque des alternatives plus simples existent.

0 votes

109voto

Alexander Prokofyev Points 14183

Vous pourriez vraiment utiliser l'expression régulière /((s).*?){n}/ pour rechercher la n-ième occurrence de la sous-chaîne s .

En C#, cela pourrait ressembler à ceci :

public static class StringExtender
{
    public static int NthIndexOf(this string target, string value, int n)
    {
        Match m = Regex.Match(target, "((" + Regex.Escape(value) + ").*?){" + n + "}");

        if (m.Success)
            return m.Groups[2].Captures[n - 1].Index;
        else
            return -1;
    }
}

Note : J'ai ajouté Regex.Escape à la solution originale pour permettre la recherche de caractères qui ont une signification spéciale pour le moteur regex.

2 votes

Vous devriez échapper à la value ? Dans mon cas, je cherchais un point msdn.microsoft.com/fr/us/library/

3 votes

Cette Regex ne fonctionne pas si la chaîne cible contient des sauts de ligne. Pourriez-vous la corriger ? Merci.

0 votes

Il semble se bloquer s'il n'y a pas une Nième correspondance. J'avais besoin de limiter une valeur séparée par des virgules à 1000 valeurs, et cela s'est bloqué quand le csv en avait moins. Donc @Yogesh -- probablement pas une grande réponse acceptée telle quelle. ;) En utilisant une variante de cette réponse (il existe une version chaîne à chaîne ici ) et changement de la boucle pour qu'elle s'arrête au nième compte à la place.

53voto

Jon Skeet Points 692016

C'est en gros ce que vous devez faire - ou du moins, c'est la solution la plus simple. Tout ce que vous "gaspilleriez", c'est le coût de n invocations de méthodes - vous ne vérifierez en fait pas deux fois chaque cas, si vous y réfléchissez bien. (IndexOf reviendra dès qu'il aura trouvé la correspondance, et vous continuerez là où il s'est arrêté).

2 votes

Je suppose que vous avez raison, il semble qu'il devrait y avoir une méthode intégrée, je suis sûr que c'est un cas courant.

4 votes

Vraiment ? Je ne me souviens pas avoir eu à le faire en 13 ans de développement en Java et C#. Cela ne veut pas dire que je n'ai jamais eu à le faire, mais juste pas assez souvent pour m'en souvenir.

0 votes

En parlant de Java, nous avons StringUtils.ordinalIndexOf() . C#, avec toutes les fonctionnalités de Linq et autres, n'a pas de support intégré pour cela. Et oui, il est très impératif d'avoir ce support si vous avez affaire à des analyseurs et des tokenizers.

19voto

Tod Thomson Points 1145

C'est en gros ce que vous devez faire - ou du moins, c'est la solution la plus simple. Tout ce que vous "gaspilleriez", c'est le coût de n invocations de méthodes - vous ne vérifierez en fait pas deux fois chaque cas, si vous y réfléchissez bien. (IndexOf reviendra dès qu'il aura trouvé la correspondance, et vous continuerez là où il s'est arrêté).

Voici l'implémentation récursive (de ce qui précède) idée ) comme méthode d'extension, imitant le format de la ou des méthodes du cadre :

public static int IndexOfNth(this string input,
                             string value, int startIndex, int nth)
{
    if (nth < 1)
        throw new NotSupportedException("Param 'nth' must be greater than 0!");
    if (nth == 1)
        return input.IndexOf(value, startIndex);
    var idx = input.IndexOf(value, startIndex);
    if (idx == -1)
        return -1;
    return input.IndexOfNth(value, idx + 1, --nth);
}

Voici également quelques tests unitaires (MBUnit) qui pourraient vous aider (à prouver que c'est correct) :

using System;
using MbUnit.Framework;

namespace IndexOfNthTest
{
    [TestFixture]
    public class Tests
    {
        //has 4 instances of the 
        private const string Input = "TestTest";
        private const string Token = "Test";

        /* Test for 0th index */

        [Test]
        public void TestZero()
        {
            Assert.Throws<NotSupportedException>(
                () => Input.IndexOfNth(Token, 0, 0));
        }

        /* Test the two standard cases (1st and 2nd) */

        [Test]
        public void TestFirst()
        {
            Assert.AreEqual(0, Input.IndexOfNth("Test", 0, 1));
        }

        [Test]
        public void TestSecond()
        {
            Assert.AreEqual(4, Input.IndexOfNth("Test", 0, 2));
        }

        /* Test the 'out of bounds' case */

        [Test]
        public void TestThird()
        {
            Assert.AreEqual(-1, Input.IndexOfNth("Test", 0, 3));
        }

        /* Test the offset case (in and out of bounds) */

        [Test]
        public void TestFirstWithOneOffset()
        {
            Assert.AreEqual(4, Input.IndexOfNth("Test", 4, 1));
        }

        [Test]
        public void TestFirstWithTwoOffsets()
        {
            Assert.AreEqual(-1, Input.IndexOfNth("Test", 8, 1));
        }
    }
}

0 votes

J'ai mis à jour mon formatage et mes scénarios de test en fonction des commentaires de Weston (merci Weston).

14voto

Schotime Points 6067
private int IndexOfOccurence(string s, string match, int occurence)
{
    int i = 1;
    int index = 0;

    while (i <= occurence && (index = s.IndexOf(match, index + 1)) != -1)
    {
        if (i == occurence)
            return index;

        i++;
    }

    return -1;
}

ou en C# avec des méthodes d'extension

public static int IndexOfOccurence(this string s, string match, int occurence)
{
    int i = 1;
    int index = 0;

    while (i <= occurence && (index = s.IndexOf(match, index + 1)) != -1)
    {
        if (i == occurence)
            return index;

        i++;
    }

    return -1;
}

5 votes

Si je ne me trompe pas, cette méthode échoue si la chaîne de caractères à faire correspondre commence à la position 0, ce qui peut être corrigé en définissant les paramètres suivants index initialement à -1.

1 votes

Vous pouvez également vérifier que les chaînes de caractères ne sont pas nulles ou vides et les faire correspondre, faute de quoi elles seront rejetées, mais c'est une décision de conception.

0 votes

Merci @PeterMajeed - si "BOB".IndexOf("B") renvoie 0, il en va de même pour cette fonction pour IndexOfOccurence("BOB", "B", 1)

1voto

user3227623 Points 1

Peut-être qu'il serait également agréable de travailler avec la String.Split() et vérifier si l'occurrence demandée se trouve dans le tableau. Si vous n'avez pas besoin de l'indice, mais de la valeur à l'indice

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X