Comment trouver la nième occurrence de caractère dans une chaîne?


95

Semblable à une question posée ici , je recherche une solution en Java.

Autrement dit, comment trouver l'index de la nième occurrence d'un caractère / chaîne à partir d'une chaîne?

Exemple: " / dossier1 / dossier2 / dossier3 / ". Dans ce cas, si je demande la 3e occurrence de la barre oblique (/), elle apparaît avant le dossier3, et j'espère renvoyer cette position d'index. Mon intention réelle est de la sous-traiter à partir de la nième occurrence d'un caractère.

Existe-t-il une méthode pratique / prête à l'emploi disponible dans l'API Java ou devons-nous écrire une petite logique par nous-mêmes pour résoudre ce problème?

Aussi,

  1. J'ai rapidement cherché si une méthode était prise en charge à cet effet sur StringUtils d' Apache Commons Lang , mais je n'en trouve aucune.
  2. Les expressions régulières peuvent-elles aider à cet égard?

2
Pour votre exemple particulier, en fonction de ce que vous voulez faire avec le résultat, il peut être plus facile de diviser la chaîne sur /, ce qui pourrait bien vous donner directement ce dont vous avez besoin?
The Archetypal Paul

@Paul: C'est aussi une bonne idée.
Gnanam

Réponses:


128

Si votre projet dépend déjà d'Apache Commons, vous pouvez utiliser StringUtils.ordinalIndexOf, sinon, voici une implémentation:

public static int ordinalIndexOf(String str, String substr, int n) {
    int pos = str.indexOf(substr);
    while (--n > 0 && pos != -1)
        pos = str.indexOf(substr, pos + 1);
    return pos;
}

Cet article a été réécrit sous forme d'article ici .


En dehors de l'erreur "off-by-one", il y a un autre grand positif dans la solution de @Jon Skeet - Avec un petit ajustement (inverser la boucle), vous pouvez également avoir la "nième occurrence de la dernière".
Karan Chadha

@KaranChadha, il en va de même dans cette solution. Changez simplement pour lastIndexOf.
aioobe

60

Je pense que la solution la plus simple pour trouver la Nième occurrence d'une chaîne est d'utiliser StringUtils.ordinalIndexOf () d'Apache Commons.

Exemple:

StringUtils.ordinalIndexOf("aabaabaa", "b", 2)  == 5

27

Deux options simples se présentent:

  • Utiliser à charAt()plusieurs reprises
  • Utiliser à indexOf()plusieurs reprises

Par exemple:

public static int nthIndexOf(String text, char needle, int n)
{
    for (int i = 0; i < text.length(); i++)
    {
        if (text.charAt(i) == needle)
        {
            n--;
            if (n == 0)
            {
                return i;
            }
        }
    }
    return -1;
}

Cela peut ne pas fonctionner aussi bien qu'une utilisation indexOfrépétée, mais il est peut-être plus simple de bien faire.


15

Vous pouvez essayer quelque chose comme ceci:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    public static void main(String[] args) {
      System.out.println(from3rd("/folder1/folder2/folder3/"));
    }

    private static Pattern p = Pattern.compile("(/[^/]*){2}/([^/]*)");

    public static String from3rd(String in) {
        Matcher m = p.matcher(in);

        if (m.matches())
            return m.group(2);
        else
            return null;
    }
}

Notez que j'ai fait quelques hypothèses dans l'expression régulière:

  • le chemin d'entrée est absolu (c'est-à-dire commence par "/");
  • vous n'avez pas besoin du 3ème "/" dans le résultat.

Comme demandé dans un commentaire, je vais essayer d'expliquer l'expression régulière: (/[^/]*){2}/([^/]*)

Visualisation des expressions régulières

  • /[^/]*est un /suivi de [^/]*(n'importe quel nombre de caractères qui ne sont pas a /),
  • (/[^/]*)regroupe l'expression précédente dans une seule entité. C'est le premier 1groupe de l'expression,
  • (/[^/]*){2}signifie que le groupe doit correspondre exactement aux {2}heures,
  • [^/]*est à nouveau un nombre quelconque de caractères qui ne sont pas un /,
  • ([^/]*)regroupe l'expression previos dans une seule entité. Il s'agit du 2nd groupe de l'expression.

De cette façon, vous n'avez qu'à obtenir la sous-chaîne qui correspond au 2ème groupe: return m.group(2);

Image courtoisie par Debuggex


1
pourriez-vous expliquer l'expression régulière en anglais simple? Comme: Un backslash suivi de tout ce qui n'est pas un backslach un nombre indéfini de fois ... Alors je ne suis pas sûr.
Ced

1
@Ced, j'ai ajouté une explication et une petite correction à regex. J'espère que c'est plus clair maintenant.
andcoz

Merci d'avoir expliqué l'expression régulière.
Vishwa Ratna le

8

J'ai apporté quelques modifications à la réponse d'aioobe et j'ai obtenu une nième version de lastIndexOf, et j'ai corrigé quelques problèmes de NPE. Voir le code ci-dessous:

public int nthLastIndexOf(String str, char c, int n) {
        if (str == null || n < 1)
            return -1;
        int pos = str.length();
        while (n-- > 0 && pos != -1)
            pos = str.lastIndexOf(c, pos - 1);
        return pos;
}

3
Je pense qu'il est raisonnable que la méthode lance un NPE si elle est donnée nullen argument. C'est le comportement le plus courant dans la bibliothèque standard.
aioobe

5
 ([.^/]*/){2}[^/]*(/)

Faites correspondre tout ce qui est suivi de / deux fois, puis à nouveau. Le troisième est celui que tu veux

L' état Matcher peut être utilisé pour dire où se trouve le dernier /


Je suis sûr que c'est une réponse très cool, mais comment puis-je l'utiliser dans mon code?
ARK

Regardez la réponse de @ andcoz (expression régulière différente, mais l'idée est la même)
The Archetypal Paul

3
public static int nth(String source, String pattern, int n) {

   int i = 0, pos = 0, tpos = 0;

   while (i < n) {

      pos = source.indexOf(pattern);
      if (pos > -1) {
         source = source.substring(pos+1);
         tpos += pos+1;
         i++;
      } else {
         return -1;
      }
   }

   return tpos - 1;
}

3

Aujourd'hui, les StringUtils d'Apache Commons Lang sont pris en charge ,

C'est le primitif:

int org.apache.commons.lang.StringUtils.ordinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal)

pour votre problème, vous pouvez coder ce qui suit: StringUtils.ordinalIndexOf(uri, "/", 3)

Vous pouvez également rechercher la dernière nième occurrence d'un caractère dans une chaîne avec la méthode lastOrdinalIndexOf .


3

Peut-être pourriez-vous y parvenir via la méthode String.split (..) également.

String str = "";
String[] tokens = str.split("/")
return tokens[nthIndex] == null 

2

Une autre approche:

public static void main(String[] args) {
    String str = "/folder1/folder2/folder3/"; 
    int index = nthOccurrence(str, '/', 3);
    System.out.println(index);
}

public static int nthOccurrence(String s, char c, int occurrence) {
    return nthOccurrence(s, 0, c, 0, occurrence);
}

public static int nthOccurrence(String s, int from, char c, int curr, int expected) {
    final int index = s.indexOf(c, from);
    if(index == -1) return -1;
    return (curr + 1 == expected) ? index : 
        nthOccurrence(s, index + 1, c, curr + 1, expected);
}

2

Cette réponse améliore la réponse de @aioobe. Deux bogues dans cette réponse ont été corrigés.
1. n = 0 doit renvoyer -1.
2. la nième occurrence a renvoyé -1, mais elle a fonctionné sur la n-1ième occurrence.

Essaye ça !

    public int nthOccurrence(String str, char c, int n) {
    if(n <= 0){
        return -1;
    }
    int pos = str.indexOf(c, 0);
    while (n-- > 1 && pos != -1)
        pos = str.indexOf(c, pos+1);
    return pos;
}

1
public class Sam_Stringnth {

    public static void main(String[] args) {
        String str="abcabcabc";
        int n = nthsearch(str, 'c', 3);
        if(n<=0)
            System.out.println("Character not found");
        else
            System.out.println("Position is:"+n);
    }
    public static int nthsearch(String str, char ch, int n){
        int pos=0;
        if(n!=0){
            for(int i=1; i<=n;i++){
                pos = str.indexOf(ch, pos)+1;
            }
            return pos;
        }
        else{
            return 0;
        }
    }
}

0
/* program to find nth occurence of a character */

import java.util.Scanner;

public class CharOccur1
{

    public static void main(String arg[])
    {
        Scanner scr=new Scanner(System.in);
        int position=-1,count=0;
        System.out.println("enter the string");
        String str=scr.nextLine();
        System.out.println("enter the nth occurence of the character");
        int n=Integer.parseInt(scr.next());
        int leng=str.length();
        char c[]=new char[leng];
        System.out.println("Enter the character to find");
        char key=scr.next().charAt(0);
        c=str.toCharArray();
        for(int i=0;i<c.length;i++)
        {
            if(c[i]==key)
            {
                count++;
                position=i;
                if(count==n)
                {
                    System.out.println("Character found");
                    System.out.println("the position at which the " + count + " ocurrence occurs is " + position);
                    return;
                }
            }
        }
        if(n>count)
        { 
            System.out.println("Character occurs  "+ count + " times");
            return;
        }
    }
}

0

Ma solution:

/**
 * Like String.indexOf, but find the n:th occurance of c
 * @param s string to search
 * @param c character to search for
 * @param n n:th character to seach for, starting with 1
 * @return the position (0-based) of the found char, or -1 if failed
 */

public static int nthIndexOf(String s, char c, int n) {
    int i = -1;
    while (n-- > 0) {
        i = s.indexOf(c, i + 1);
        if (i == -1)
            break;
    }
    return i;
}

0

Le code renvoie la nième sous-chaîne de positions d'occurrence aka largeur de champ. Exemple. si la chaîne "Stack overflow in low melow" est la chaîne pour rechercher la 2ème occurrence du jeton "low", vous conviendrez avec moi que la 2ème occurrence est à la sous-chaîne "18 et 21" . indexOfOccurance ("Stack overflow in low melow", low, 2) renvoie 18 et 21 dans une chaîne.

class Example{
    public Example(){
    }
            public String indexOfOccurance(String string, String token, int nthOccurance) {
                    int lengthOfToken = token.length();
                    int nthCount = 0;
                    for (int shift = 0,count = 0; count < string.length() - token.length() + 2; count++, shift++, lengthOfToken++)
                        if (string.substring(shift, lengthOfToken).equalsIgnoreCase(token)) { 
                    // keeps count of nthOccurance
                            nthCount++; 
                        if (nthCount == nthOccurance){
                    //checks if nthCount  == nthOccurance. If true, then breaks 
                             return String.valueOf(shift)+ " " +String.valueOf(lengthOfToken);   
                        }  
                    }
                    return "-1";
                }
    public static void main(String args[]){
    Example example = new Example();
    String string = "the man, the woman and the child";
    int nthPositionOfThe = 3;
   System.out.println("3rd Occurance of the is at " + example.indexOfOccurance(string, "the", nthPositionOfThe));
    }
    }

0
public static int findNthOccurrence(String phrase, String str, int n)
{
    int val = 0, loc = -1;
    for(int i = 0; i <= phrase.length()-str.length() && val < n; i++)
    {
        if(str.equals(phrase.substring(i,i+str.length())))
        {
            val++;
            loc = i;
        }
    }

    if(val == n)
        return loc;
    else
        return -1;
}

2
Bien que ce code puisse résoudre la question, inclure une explication sur comment et pourquoi cela résout le problème aiderait vraiment à améliorer la qualité de votre publication et entraînerait probablement plus de votes à la hausse. N'oubliez pas que vous répondez à la question des lecteurs à l'avenir, pas seulement à la personne qui la pose maintenant. Veuillez modifier votre réponse pour ajouter des explications et donner une indication des limites et des hypothèses applicables.
Pika le magicien des baleines
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.