Come trovare l'ennesima occorrenza di carattere in una stringa?


95

Simile a una domanda pubblicata qui , sto cercando una soluzione in Java.

Cioè, come trovare l'indice dell'ennesima occorrenza di un carattere / stringa da una stringa?

Esempio: " / cartella1 / cartella2 / cartella3 / ". In questo caso, se chiedo la terza occorrenza di barra (/), appare prima della cartella3 e mi aspetto di restituire questa posizione di indice. La mia vera intenzione è di sottostrarla dall'ennesima occorrenza di un personaggio.

C'è qualche metodo conveniente / pronto all'uso disponibile nell'API Java o abbiamo bisogno di scrivere una piccola logica da soli per risolvere questo problema?

Anche,

  1. Ho cercato rapidamente se qualche metodo è supportato per questo scopo su StringUtils di Apache Commons Lang , ma non ne trovo.
  2. Le espressioni regolari possono aiutare in questo senso?

2
Per il tuo esempio particolare, a seconda di cosa vuoi fare con il risultato, potrebbe essere più facile dividere la stringa su /, che potrebbe darti ciò di cui hai bisogno direttamente?
L'archetipo Paolo

@ Paul: Anche questa è una buona idea.
Gnanam

Risposte:


128

Se il tuo progetto dipende già da Apache Commons puoi usare StringUtils.ordinalIndexOf, altrimenti ecco un'implementazione:

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

Questo post è stato riscritto come articolo qui .


A parte l'errore "off-by-one", c'è un altro grande positivo nella soluzione di @Jon Skeet - Con un piccolo aggiustamento (invertendo il ciclo), puoi anche avere l '"ennesima occorrenza dall'ultima".
Karan Chadha

@KaranChadha, lo stesso vale per questa soluzione. Basta passare a lastIndexOf.
aioobe

60

Credo che la soluzione più semplice per trovare l'ennesima occorrenza di una stringa sia usare StringUtils.ordinalIndexOf () da Apache Commons.

Esempio:

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

27

Si verificano due semplici opzioni:

  • Usa charAt()ripetutamente
  • Usa indexOf()ripetutamente

Per esempio:

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

Potrebbe non funzionare come se fosse usato indexOfripetutamente, ma forse è più semplice farlo bene.


15

Puoi provare qualcosa di simile:

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

Nota che ho fatto alcune ipotesi nella regex:

  • il percorso di input è assoluto (cioè inizia con "/");
  • non è necessario il terzo "/" nel risultato.

Come richiesto in un commento, cercherò di spiegare la regex: (/[^/]*){2}/([^/]*)

Visualizzazione di espressioni regolari

  • /[^/]*è /seguito da [^/]*(qualsiasi numero di caratteri che non sia a /),
  • (/[^/]*)raggruppa l'espressione precedente in una singola entità. Questo è il 1primo gruppo dell'espressione,
  • (/[^/]*){2}significa che il gruppo deve corrispondere esattamente {2}volte,
  • [^/]*è ancora un numero qualsiasi di caratteri che non sono un /,
  • ([^/]*)raggruppa l'espressione precedente in una singola entità. Questo è il 2nd gruppo dell'espressione.

In questo modo devi solo ottenere la sottostringa che corrisponde al 2 ° gruppo: return m.group(2);

Immagine per gentile concessione di Debuggex


1
potresti spiegare la regex in un inglese semplice? Ad esempio: una barra rovesciata seguita da tutto ciò che non è una barra rovesciata per un numero indefinito di tempo ... Quindi non sono sicuro.
Ced

1
@Ced, ho aggiunto una spiegazione e una piccola correzione alla regex. Spero sia più chiaro ora.
andcoz

Grazie per aver spiegato la regex.
Vishwa Ratna,

8

Ho apportato alcune modifiche alla risposta di aioobe e ho ottenuto un'ennesima versione di lastIndexOf e ho risolto alcuni problemi di NPE. Vedere il codice di seguito:

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
Penso che sia ragionevole che il metodo generi un NPE se fornito nullcome argomento. Questo è il comportamento più comune nella libreria standard.
aioobe

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

Abbina qualsiasi cosa seguita da / due volte, poi di nuovo. Il terzo è quello che vuoi

Lo stato Matcher può essere utilizzato per dire dove si trova l'ultima /


Sono sicuro che questa sia una risposta molto interessante, ma come la uso nel mio codice?
ARK

Guarda la risposta di @ andcoz (espressione regolare diversa, ma l'idea è la stessa)
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

Al giorno d'oggi c'è il supporto di StringUtils di Apache Commons Lang ,

Questa è la primitiva:

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

per il tuo problema puoi codificare quanto segue: StringUtils.ordinalIndexOf(uri, "/", 3)

Puoi anche trovare l'ultima ennesima occorrenza di un carattere in una stringa con il metodo lastOrdinalIndexOf .


3

Potrebbe essere possibile ottenere questo risultato anche tramite il metodo String.split (..).

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

2

Un altro approccio:

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

Questa risposta migliora la risposta di @aioobe. Due bug in quella risposta sono stati corretti.
1. n = 0 dovrebbe restituire -1.
2. l'ennesima occorrenza ha restituito -1, ma ha funzionato su n-1-occorrenza.

Prova questo !

    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

La mia soluzione:

/**
 * 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

Il codice restituisce l'ennesima posizione della sottostringa, ovvero la larghezza del campo. Esempio. se la stringa "Stack overflow in low melow" è la stringa per cercare la seconda occorrenza del token "basso", sarai d'accordo con me che la seconda occorrenza è alla sottrazione "18 e 21" . indexOfOccurance ("Stack overflow in low melow", low, 2) restituisce 18 e 21 in una stringa.

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
Sebbene questo codice possa risolvere la domanda, inclusa una spiegazione di come e perché questo risolve il problema aiuterebbe davvero a migliorare la qualità del tuo post e probabilmente si tradurrebbe in più voti positivi. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo alla persona che chiede ora. Modifica la tua risposta per aggiungere spiegazioni e fornire un'indicazione di quali limitazioni e ipotesi si applicano.
Pika il mago delle balene
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.