Wie finde ich das n-te Vorkommen eines Zeichens in einer Zeichenkette?

95

Ähnlich wie bei einer Frage gepostet hier , ist auf der Suche nach einer Lösung in Java.

Das heißt, wie findet man den Index des n-ten Auftretens eines Zeichens / einer Zeichenfolge aus einer Zeichenfolge?

Beispiel: " / folder1 / folder2 / folder3 / ". Wenn ich in diesem Fall nach dem dritten Auftreten eines Schrägstrichs (/) frage, wird dieser vor Ordner3 angezeigt, und ich erwarte, diese Indexposition zurückzugeben. Meine eigentliche Absicht ist es, es vom n-ten Auftreten eines Zeichens zu trennen.

Gibt es eine bequeme / gebrauchsfertige Methode in der Java-API oder müssen wir selbst eine kleine Logik schreiben, um dies zu lösen?

Ebenfalls,

  1. Ich habe schnell gesucht, ob eine Methode für diesen Zweck bei Apache Commons Langs StringUtils unterstützt wird , aber ich finde keine.
  2. Können reguläre Ausdrücke dabei helfen?
Gnanam
quelle
2
Für Ihr spezielles Beispiel ist es je nachdem, was Sie mit dem Ergebnis tun möchten, möglicherweise einfacher, die Zeichenfolge auf / zu teilen, wodurch Sie möglicherweise direkt das erhalten, was Sie benötigen.
Der archetypische Paul
@ Paul: Das ist auch eine gute Idee.
Gnanam

Antworten:

128

Wenn Ihr Projekt bereits von Apache Commons abhängt, können Sie es verwenden StringUtils.ordinalIndexOf. Andernfalls finden Sie hier eine Implementierung:

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

Dieser Beitrag wurde bereits als ein Artikel neu geschrieben hier .

aioobe
quelle
Abgesehen von dem "off-by-one" -Fehler gibt es in @Jon Skeets Lösung noch einen weiteren großen positiven Effekt: Mit einer kleinen Änderung (Umkehren der Schleife) können Sie auch das "n-te Vorkommen vom letzten" haben.
Karan Chadha
@KaranChadha, das gilt auch für diese Lösung. Wechseln Sie einfach zu lastIndexOf.
Aioobe
60

Ich glaube, die einfachste Lösung, um das N-te Vorkommen eines Strings zu finden, ist die Verwendung von StringUtils.ordinalIndexOf () von Apache Commons.

Beispiel:

StringUtils.ordinalIndexOf("aabaabaa", "b", 2)  == 5
Al Belsky
quelle
27

Es gibt zwei einfache Optionen:

  • verwenden Sie charAt()wiederholt
  • verwenden Sie indexOf()wiederholt

Beispielsweise:

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

Das funktioniert vielleicht nicht so gut wie das indexOfwiederholte Verwenden , aber es ist möglicherweise einfacher, es richtig zu machen.

Jon Skeet
quelle
15

Sie können so etwas ausprobieren:

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

Beachten Sie, dass ich im regulären Ausdruck einige Annahmen getroffen habe:

  • der Eingabepfad ist absolut (dh beginnt mit "/");
  • Sie benötigen nicht das 3. "/" im Ergebnis.

Wie in einem Kommentar angefordert, werde ich versuchen, den regulären Ausdruck zu erklären: (/[^/]*){2}/([^/]*)

Visualisierung regulärer Ausdrücke

  • /[^/]*ist ein /gefolgt von [^/]*(eine beliebige Anzahl von Zeichen, die kein a sind /),
  • (/[^/]*)gruppiert den vorherigen Ausdruck in einer einzelnen Entität. Dies ist die 1erste Gruppe des Ausdrucks.
  • (/[^/]*){2}bedeutet, dass die Gruppe genau übereinstimmen muss {2},
  • [^/]*ist wieder eine beliebige Anzahl von Zeichen, die nicht a /,
  • ([^/]*)gruppiert den vorherigen Ausdruck in einer einzigen Entität. Dies ist die 2zweite Gruppe des Ausdrucks.

Auf diese Weise müssen Sie nur den Teilstring erhalten, der der 2. Gruppe entspricht: return m.group(2);

Bild mit freundlicher Genehmigung von Debuggex

andcoz
quelle
1
Könntest du den regulären Ausdruck in einfachem Englisch erklären? Wie: Ein Backslash, gefolgt von etwas, das auf unbestimmte Zeit kein Backslach ist ... Dann bin ich mir nicht sicher.
Ced
1
@Ced, ich habe eine Erklärung und eine kleine Korrektur zu Regex hinzugefügt. Ich hoffe es ist jetzt klarer.
andcoz
Vielen Dank für die Erklärung der Regex.
Vishwa Ratna
8

Ich habe ein paar Änderungen an der Antwort von aioobe vorgenommen und eine n-te lastIndexOf-Version erhalten und einige NPE-Probleme behoben. Siehe Code unten:

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;
}
Doof
quelle
3
Ich denke, es ist vernünftig, dass die Methode eine NPE auslöst, wenn sie nullals Argument angegeben wird. Dies ist das häufigste Verhalten in der Standardbibliothek.
Aioobe
5
 ([.^/]*/){2}[^/]*(/)

Passen Sie alles an, gefolgt von / zweimal, dann noch einmal. Der dritte ist der, den Sie wollen

Der Matcher- Status kann verwendet werden, um festzustellen , wo sich das letzte / befindet

Der archetypische Paulus
quelle
Ich bin mir sicher, dass dies eine sehr coole Antwort ist, aber wie verwende ich das in meinem Code?
ARK
Schauen Sie sich die Antwort von @ andcoz an (anderer regulärer Ausdruck, aber die Idee ist dieselbe)
Der archetypische Paulus
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;
}
Saul
quelle
3

Heutzutage gibt es Unterstützung für Apache Commons Langs StringUtils ,

Dies ist das Grundelement:

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

Für Ihr Problem können Sie Folgendes codieren: StringUtils.ordinalIndexOf(uri, "/", 3)

Sie können auch das letzte n-te Vorkommen eines Zeichens in einer Zeichenfolge mit der lastOrdinalIndexOf- Methode finden.

Chexpir
quelle
3

Möglicherweise können Sie dies auch über die String.split (..) -Methode erreichen.

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

Ein anderer Ansatz:

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);
}
Marimuthu Madasamy
quelle
2

Diese Antwort verbessert die Antwort von @aioobe. Zwei Fehler in dieser Antwort wurden behoben.
1. n = 0 sollte -1 zurückgeben.
2. Das n-te Vorkommen gab -1 zurück, aber es funktionierte bei n-1. Vorkommen.

Versuche dies !

    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;
}
ARCHE
quelle
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;
        }
    }
}
SAN
quelle
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;
        }
    }
}
Rose
quelle
0

Meine Lösung:

/**
 * 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;
}
Per Lindberg
quelle
0

Der Code gibt die Teilzeichenfolge für n-te Vorkommenspositionen, auch Feldbreite genannt, zurück. Beispiel. Wenn die Zeichenfolge "Stapelüberlauf in niedrigem Melow" die Zeichenfolge für die Suche nach dem zweiten Vorkommen von Token "niedrig" ist, stimmen Sie mir zu, dass das zweite Vorkommen "18 und 21" unterschreitet . indexOfOccurance ("Stapelüberlauf bei niedrigem Melow", niedrig, 2) gibt 18 und 21 in einer Zeichenfolge zurück.

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

quelle
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;
}
wess
quelle
2
Während dieser Code die Frage lösen kann, einschließlich einer Erklärung, wie und warum dies das Problem löst, würde dies wirklich dazu beitragen, die Qualität Ihres Beitrags zu verbessern, und wahrscheinlich zu mehr Up-Votes führen. Denken Sie daran, dass Sie in Zukunft die Frage für die Leser beantworten, nicht nur für die Person, die jetzt fragt. Bitte bearbeiten Sie Ihre Antwort, um Erklärungen hinzuzufügen und anzugeben, welche Einschränkungen und Annahmen gelten.
Pika der Zauberer der Wale