2010-10-20 3 views
74

Simile a una domanda pubblicata here, sto cercando per una soluzione in Java.Come trovare l'ennesima occorrenza di carattere in una stringa?

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 della barra (/), appare prima della cartella3, e mi aspetto di restituire questa posizione dell'indice. La mia intenzione attuale è di sottostrarla dall'ennesima occorrenza di un personaggio.

Esiste un metodo pratico/pronto per l'uso disponibile nell'API Java o è necessario scrivere una piccola logica per risolvere il problema?

Inoltre,

  1. ho subito cercato se qualsiasi metodo è supportato per questo scopo in Apache Commons Lang StringUtils, ma non trovo alcuna.
  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ù semplice dividere la stringa su /, che potrebbe darti esattamente quello che ti serve direttamente? –

+0

@Paul: anche questa è una buona idea. – Gnanam

risposta

25

due semplici opzioni si verificano:

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

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

che potrebbe non eseguire così come u canta ripetutamente indexOf, ma è probabilmente più semplice avere ragione.

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

Nessuna corrispondenza seguita da/due volte, quindi di nuovo. Il terzo è quello che si desidera

Lo stato Matcher può essere utilizzato per indicare dove l'ultimo/è

+0

Sono sicuro che questa è una risposta molto interessante, ma come posso utilizzarla nel mio codice? –

+0

Guarda la risposta di @ andcoz (regexp diverso, ma l'idea è la stessa) –

110

Se il tuo progetto dipende già su Apache Commons è possibile utilizzare StringUtils.ordinalIndexOf, in caso contrario, 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 un articolo here.

+0

Oltre all'errore "off-by-one", c'è un altro grande positivo nella soluzione di @Jon Skeet - Con un piccolo aggiustamento (inversione del ciclo) , puoi anche avere l '"ennesima ricorrenza dall'ultima". –

+0

@KaranChadha, lo stesso vale per questa soluzione. Basta passare a ['lastIndexOf'] (https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#lastIndexOf%28java.lang.String,%20int%29). – aioobe

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

Si può 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; 
    } 
} 

Si noti che ho fatto alcune assunzioni nella regex:

  • il percorso di ingresso è assoluta (cioè inizia con "/") ;
  • non è necessario il 3 °/"" nel risultato.

Come richiesto in un commento, cercherò di spiegare l'espressione regolare: (/[^/]*){2}/([^/]*)

Regular expression visualization

  • /[^/]* è un / seguito da [^/]* (qualsiasi numero di caratteri che sono non a /),
  • (/[^/]*) gruppi th e espressione precedente in una singola entità. Questo è il 1 ° gruppo dell'espressione,
  • (/[^/]*){2} significa che il gruppo deve corrispondere extactly {2} volte,
  • [^/]* è di nuovo un numero qualsiasi di caratteri che non sono una /,
  • ([^/]*) gruppi l'espressione previos in un singola entità. Questo è il gruppo 2 ndell'espressione.

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

Immagine gentilmente concessa da Debuggex

+1

potresti spiegare la regex in inglese normale? Come: una barra rovesciata seguita da tutto ciò che non è un backslach un numero indefinito di tempo ... Quindi non sono sicuro. – Ced

+1

@Ced, ho aggiunto una spiegazione e una piccola correzione per regex. Spero che ora sia più chiaro. – andcoz

+0

Questo è stato molto chiaro, grazie. – Ced

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

credo che la soluzione più semplice per la ricerca dell'ennesima occorrenza di una stringa consiste nell'utilizzare StringUtils.ordinalIndexOf() da Apache Commons.

Esempio:

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

Ho fatto alcune modifiche per aioobe di rispondere e ha ottenuto una versione lastIndexOf ennesima, e risolvere alcuni problemi NPE. Vedere codice qui sotto:

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

Penso che sia ragionevole che il metodo lanci un NPE se viene dato "null" come argomento. Questo è il comportamento più comune nella libreria standard. – aioobe

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

// in puro C++

int pos = 0; 
for (int i = 0; i < N; ++i) // N = nth position 
{ 
    pos = STRING.find(delim, pos + size_of_delim); 
} 
3

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

Questa è la primitiva Apache Commons Lang:

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

per il vostro problema è possibile codificare il seguente: StringUtils.ordinalIndexOf(uri, "/", 3)

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

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

Questa risposta migliora sulla risposta di @aioobe. Sono stati corretti due bug in quella risposta.
1. n = 0 deve restituire -1.
2. l'ennesima occorrenza restituisce -1, ma ha funzionato su n-1 ° occorrenze.

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; 
} 
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 le posizioni N-esima occorrenza di sottostringa aka larghezza del campo. Esempio. se stringa "Stack overflow in low melow" è la stringa per cercare 2nd occured di token "low", sarete d'accordo con me che è occorrenza 2a sottotitolazione "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

Potrebbe essere possibile ottenere ciò tramite il metodo String.split (..).

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