Vorkommen von Teilzeichenfolgen in einer Zeichenfolge

122

Warum hält der folgende Algorithmus für mich nicht an? (str ist die Zeichenfolge, in der ich suche, findStr ist die Zeichenfolge, die ich zu finden versuche)

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int lastIndex = 0;
int count = 0;

while (lastIndex != -1) {
    lastIndex = str.indexOf(findStr,lastIndex);

    if( lastIndex != -1)
        count++;

    lastIndex += findStr.length();
}

System.out.println(count);
Robert Harvey
quelle
8
Wir haben in Udacity einen wirklich guten gemacht: Wir haben newSTR = str.replace (findStr, "") verwendet; und zurückgegebene Anzahl = ((str.length () - newSTR.length ()) / findStr.length ());
SolarLunix
Ähnliche Frage für Zeichen: stackoverflow.com/q/275944/873282
koppor
Möchten Sie nicht auch den Fall berücksichtigen, in dem das Präfix der Suchzeichenfolge das Suffix ist? In diesem Fall glaube ich nicht, dass eine der vorgeschlagenen Antworten funktionieren würde. Hier ist ein Beispiel. In diesem Fall würden Sie einen ausgefeilteren Algorithmus benötigen, wie den Knuth Morris Pratt (KMP), der im CLRS-Buch
Sid
Es wird für Sie nicht angehalten, da Sie es nach Erreichen Ihrer 'Halt'-Bedingung (lastIndex == -1) zurücksetzen, indem Sie den Wert von lastIndex (lastIndex + = findStr.length ();)
Legna

Antworten:

83

Die letzte Zeile verursachte ein Problem. lastIndexwürde niemals bei -1 sein, also würde es eine Endlosschleife geben. Dies kann behoben werden, indem die letzte Codezeile in den if-Block verschoben wird.

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int lastIndex = 0;
int count = 0;

while(lastIndex != -1){

    lastIndex = str.indexOf(findStr,lastIndex);

    if(lastIndex != -1){
        count ++;
        lastIndex += findStr.length();
    }
}
System.out.println(count);
Codebreach
quelle
121
Diese Antwort ist die genaue Kopie des Beitrags, den ich eine Stunde zuvor gemacht habe;)
Olivier
8
Beachten Sie, dass dies möglicherweise das erwartete Ergebnis zurückgibt oder nicht. Mit der Teilzeichenfolge "aa" und der Zeichenfolge für die Suche nach "aaa" kann die Anzahl der erwarteten Vorkommen eins sein (von diesem Code zurückgegeben), aber auch zwei (in diesem Fall benötigen Sie "lastIndex ++" anstelle von "lastIndex + =" findStr.length () ") je nachdem, wonach Sie suchen.
Stanislav Kniazev
@olivier hat das nicht gesehen ... :( @stan das ist absolut richtig ... ich habe gerade den Code im Problem behoben ... denke, es hängt davon ab, was Bobcom mit der Anzahl der Vorkommen in der Zeichenfolge meint ...
Codebreach
1
Wann werden die Leute lernen, solche Dinge in eine statische Methode zum Kopieren und Einfügen zu verpacken? Siehe meine Antwort unten, es ist auch optimierter.
mmm
1
Die Moral hier ist, wenn Sie beabsichtigen, eine Antwort zu schreiben, prüfen Sie zuerst, ob jemand anderes bereits genau die gleiche Antwort geschrieben hat oder nicht. Es hat wirklich keinen Vorteil, wenn dieselbe Antwort zweimal angezeigt wird, unabhängig davon, ob Ihre Antwort kopiert oder unabhängig geschrieben wurde.
Dawood ibn Kareem
191

Wie wäre es mit StringUtils.countMatches von Apache Commons Lang?

String str = "helloslkhellodjladfjhello";
String findStr = "hello";

System.out.println(StringUtils.countMatches(str, findStr));

Das gibt aus:

3
A_M
quelle
9
Egal wie richtig dieser Vorschlag ist, er kann nicht als Lösung akzeptiert werden, da er die Frage von OP nicht beantwortet
kommradHomer
3
Ist das veraltet oder so .. meine IDE erkennt nicht
Vamsi Pavan Mahesh
@VamsiPavanMahesh StringUtils ist eine Bibliothek von Apache Commons. Überprüfen Sie hier: commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/…
Anup
Diese Antwort ist eine Kopie von Peter Lawreys Antwort einen Tag zuvor (siehe unten).
Zon
StringUtilshat keine countMatchesMethode.
Plaidshirt
117

Ihr lastIndex += findStr.length();wurde außerhalb der Klammern platziert, was eine Endlosschleife verursachte (wenn kein Vorkommen gefunden wurde, war lastIndex immer zu findStr.length()).

Hier ist die feste Version:

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int lastIndex = 0;
int count = 0;

while (lastIndex != -1) {

    lastIndex = str.indexOf(findStr, lastIndex);

    if (lastIndex != -1) {
        count++;
        lastIndex += findStr.length();
    }
}
System.out.println(count);
Olivier
quelle
92

Eine kürzere Version. ;)

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
System.out.println(str.split(findStr, -1).length-1);
Peter Lawrey
quelle
8
return haystack.split(Pattern.quote(needle), -1).length - 1;wenn zum Beispielneedle=":)"
Mr_and_Mrs_D
2
@lOranger Ohne das ,-1werden nachfolgende Übereinstimmungen gelöscht .
Peter Lawrey
3
Autsch, danke, gut zu wissen! Dies wird mich lehren, die kleinen Zeilen im Javadoc zu lesen ...
Laurent Grégoire
4
Nett! Aber es enthält nur nicht überlappende Übereinstimmungen, nicht wahr? Wenn beispielsweise "aa" in "aaa" übereinstimmt, wird 1 zurückgegeben, nicht 2? Natürlich sind überlappende oder nicht überlappende Übereinstimmungen sowohl gültig als auch abhängig von den Benutzeranforderungen (möglicherweise ein Flag, um Zählüberschneidungen anzuzeigen, ja / nein)?
Cornel Masson
2
-1 .. versuchen Sie dies auf "aaaa" und "aa" auszuführen .. die richtige Antwort ist 3 nicht 2.
Kalyanaraman Santhanam
79

Müssen Sie das Matching wirklich selbst erledigen? Besonders wenn Sie nur die Anzahl der Vorkommen benötigen, sind reguläre Ausdrücke aufgeräumter:

String str = "helloslkhellodjladfjhello";
Pattern p = Pattern.compile("hello");
Matcher m = p.matcher(str);
int count = 0;
while (m.find()){
    count +=1;
}
System.out.println(count);     
Jean
quelle
1
Dies findet keine Sonderzeichen, es wird 0 Anzahl für Zeichenfolgen unten finden: String str = "hel+loslkhel+lodjladfjhel+lo"; Pattern p = Pattern.compile("hel+lo");
Ben
13
Ja, wenn Sie Ihre Regex korrekt ausdrücken. Versuch mit Pattern.compile("hel\\+lo");dem +Zeichen hat eine besondere Bedeutung in einem regulären Ausdruck und muss maskiert werden.
Jean
4
Wenn Sie einen beliebigen String nehmen und ihn als exakte Übereinstimmung mit allen ignorierten Sonderzeichen für reguläre Ausdrücke verwenden möchten, Pattern.quote(str)ist dies Ihr Freund!
Mike Furtak
2
Dies funktioniert nicht für "aaa", wenn str = "aaaaaa". Es gibt 4 Antworten, aber Ihre geben 2
Pujan Srivastava
Diese Lösung funktioniert in diesem Fall nicht: str = "Dies ist ein Test \\ n \\ r string", subStr = "\\ r", es werden 0 Vorkommen angezeigt.
Maksym Ovsianikov
19

Ich bin sehr überrascht, dass niemand diesen einen Liner erwähnt hat. Es ist einfach, prägnant und bietet eine etwas bessere Leistung alsstr.split(target, -1).length-1

public static int count(String str, String target) {
    return (str.length() - str.replace(target, "").length()) / target.length();
}
kmecpp
quelle
Sollte die beste Antwort sein. Danke dir!
Lakam99
12

Hier ist es, eingepackt in eine schöne und wiederverwendbare Methode:

public static int count(String text, String find) {
        int index = 0, count = 0, length = find.length();
        while( (index = text.indexOf(find, index)) != -1 ) {                
                index += length; count++;
        }
        return count;
}
mmm
quelle
8
String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int lastIndex = 0;
int count = 0;

while((lastIndex = str.indexOf(findStr, lastIndex)) != -1) {
     count++;
     lastIndex += findStr.length() - 1;
}
System.out.println(count);

am Ende der Schleife beträgt die Anzahl 3; ich hoffe es hilft

dfa
quelle
5
Der Code enthält einen Fehler. Wenn wir nach einem einzelnen Zeichen suchen, findStr.length() - 1gibt das 0 zurück und wir befinden uns in einem endlosen Zyklus.
Jan Bodnar
6

Viele der gegebenen Antworten schlagen bei einer oder mehreren der folgenden fehl:

  • Muster beliebiger Länge
  • Überlappende Übereinstimmungen (z. B. "232" in "23232" oder "aa" in "aaa" zählen)
  • Metazeichen für reguläre Ausdrücke

Folgendes habe ich geschrieben:

static int countMatches(Pattern pattern, String string)
{
    Matcher matcher = pattern.matcher(string);

    int count = 0;
    int pos = 0;
    while (matcher.find(pos))
    {
        count++;
        pos = matcher.start() + 1;
    }

    return count;
}

Beispielaufruf:

Pattern pattern = Pattern.compile("232");
int count = countMatches(pattern, "23232"); // Returns 2

Wenn Sie eine Suche mit nicht regulären Ausdrücken wünschen, kompilieren Sie Ihr Muster einfach entsprechend mit dem LITERALFlag:

Pattern pattern = Pattern.compile("1+1", Pattern.LITERAL);
int count = countMatches(pattern, "1+1+1"); // Returns 2
benkc
quelle
Ja ... überrascht, dass es so etwas in Apache StringUtils nicht gibt.
Mike Nagetier
6
public int countOfOccurrences(String str, String subStr) {
  return (str.length() - str.replaceAll(Pattern.quote(subStr), "").length()) / subStr.length();
}
Maksym Ovsianikov
quelle
Gute Antwort. Kann es Ihnen etwas ausmachen, einige Anmerkungen zur Funktionsweise hinzuzufügen?
Santhosh Kumar
Sicher, str - ist unser Quellstring, subStr - ist ein Teilstring. Ziel ist es, die Anzahl der Vorkommen von subStr in str zu berechnen. Dazu verwenden wir die Formel: (ab) / c, wobei a - Länge von str, b - Länge von str ohne alle Vorkommen von subStr (wir entfernen hierfür alle Vorkommen von subStr aus str), c - Länge von subStr . Also extrahieren wir im Grunde genommen aus der Länge von str - Länge von str ohne alle subStr und teilen dann das Ergebnis auf die Länge von subStr. Bitte lassen Sie mich wissen, wenn Sie weitere Fragen haben.
Maksym Ovsianikov
Santhosh, du bist willkommen! Der wichtige Teil ist die Verwendung von Pattern.quote für subStr. Andernfalls kann in einigen Fällen ein Fehler auftreten, z. B. in diesem Fall: str = "Dies ist ein Test \\ n \\ r string", subStr = "\\ r". Einige ähnliche Antworten, die hier bereitgestellt werden, verwenden kein Muster, daher schlagen sie in solchen Fällen fehl.
Maksym Ovsianikov
Es gibt keinen Grund für Regex, Verwendung replace, nicht replaceAll.
NateS
3

Zuwachs lastIndex Sie, wann immer Sie nach dem nächsten Auftreten suchen.

Andernfalls wird immer der erste Teilstring gefunden (an Position 0).

Stanislav Kniazev
quelle
3
public int indexOf(int ch,
                   int fromIndex)

Gibt den Index innerhalb dieser Zeichenfolge des ersten Vorkommens des angegebenen Zeichens zurück und startet die Suche am angegebenen Index.

Ihr lastindexWert ist also immer 0 und er findet immer Hallo in der Zeichenfolge.

Bhushan Bhangale
quelle
2

Die als richtig gegebene Antwort eignet sich nicht zum Zählen von Zeilenrückläufen und ist viel zu ausführlich. Spätere Antworten sind besser, aber alles kann einfach mit erreicht werden

str.split(findStr).length

Nach dem Beispiel in der Frage werden keine nachfolgenden Übereinstimmungen gelöscht.

Kennzeichen
quelle
1
Dies wurde bereits in einer anderen Antwort behandelt ; und diese Antwort hat es auch besser gemacht.
michaelb958 - GoFundMonica
1
Dies sollte ein Kommentar zu der fraglichen Antwort sein, keine andere Antwort.
James.garriss
2

Sie können die Anzahl der Vorkommen mithilfe der integrierten Bibliotheksfunktion festlegen:

import org.springframework.util.StringUtils;
StringUtils.countOccurrencesOf(result, "R-")
Sieger
quelle
1
Funktioniert nicht, sollten Sie die von Ihnen verwendete Abhängigkeit angeben.
Saikat
1

Versuchen Sie lastIndex+=findStr.length(), am Ende Ihrer Schleife etwas hinzuzufügen , da Sie sonst in einer Endlosschleife landen, da Sie, sobald Sie den Teilstring gefunden haben, versuchen, ihn immer wieder von derselben letzten Position aus zu finden.

Thorsten Schleinzer
quelle
1

Probier diese. Es ersetzt alle Übereinstimmungen durch a -.

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int numberOfMatches = 0;
while (str.contains(findStr)){
    str = str.replaceFirst(findStr, "-");
    numberOfMatches++;
}

Und wenn Sie Ihre nicht zerstören möchten, können strSie eine neue Zeichenfolge mit demselben Inhalt erstellen:

String str = "helloslkhellodjladfjhello";
String strDestroy = str;
String findStr = "hello";
int numberOfMatches = 0;
while (strDestroy.contains(findStr)){
    strDestroy = strDestroy.replaceFirst(findStr, "-");
    numberOfMatches++;
}

Nach dem Ausführen dieses Blocks sind dies Ihre Werte:

str = "helloslkhellodjladfjhello"
strDestroy = "-slk-djladfj-"
findStr = "hello"
numberOfMatches = 3
Xander
quelle
1

Wie @Mr_and_Mrs_D vorgeschlagen hat:

String haystack = "hellolovelyworld";
String needle = "lo";
return haystack.split(Pattern.quote(needle), -1).length - 1;
Ron Tesler
quelle
1

Basierend auf den vorhandenen Antworten möchte ich eine "kürzere" Version ohne das if hinzufügen:

String str = "helloslkhellodjladfjhello";
String findStr = "hello";

int count = 0, lastIndex = 0;
while((lastIndex = str.indexOf(findStr, lastIndex)) != -1) {
    lastIndex += findStr.length() - 1;
    count++;
}

System.out.println(count); // output: 3
sjkm
quelle
Dieser berücksichtigt, wenn sich die Zeichenfolge wiederholt, z. B. wenn Sie nach der Zeichenfolge 'xx' in einer Zeichenfolge 'xxx' suchen.
tCoe
1

Hier ist die erweiterte Version zum Zählen, wie oft das Token in einer vom Benutzer eingegebenen Zeichenfolge aufgetreten ist:

public class StringIndexOf {

    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);

        System.out.println("Enter a sentence please: \n");
        String string = scanner.nextLine();

        int atIndex = 0;
        int count = 0;

        while (atIndex != -1)
        {
            atIndex = string.indexOf("hello", atIndex);

            if(atIndex != -1)
            {
                count++;
                atIndex += 5;
            }
        }

        System.out.println(count);
    }

}
Venzentx
quelle
1

Die folgende Methode zeigt, wie oft sich Teilzeichenfolgen auf Ihrer gesamten Zeichenfolge wiederholen. Hoffe, Sie voll zu nutzen: -

    String searchPattern="aaa"; // search string
    String str="aaaaaababaaaaaa"; // whole string
    int searchLength = searchPattern.length(); 
    int totalLength = str.length(); 
    int k = 0;
    for (int i = 0; i < totalLength - searchLength + 1; i++) {
        String subStr = str.substring(i, searchLength + i);
        if (subStr.equals(searchPattern)) {
           k++;
        }

    }
Duggu
quelle
0

Hier ist die andere Lösung, ohne Regexp / Patterns / Matcher zu verwenden oder StringUtils nicht zu verwenden.

String str = "helloslkhellodjladfjhelloarunkumarhelloasdhelloaruhelloasrhello";
        String findStr = "hello";
        int count =0;
        int findStrLength = findStr.length();
        for(int i=0;i<str.length();i++){
            if(findStr.startsWith(Character.toString(str.charAt(i)))){
                if(str.substring(i).length() >= findStrLength){
                    if(str.substring(i, i+findStrLength).equals(findStr)){
                        count++;
                    }
                }
            }
        }
        System.out.println(count);
Arun Kumar Mudraboyina
quelle
0

Wenn Sie den Index jeder Teilzeichenfolge in der ursprünglichen Zeichenfolge benötigen, können Sie mit indexOf Folgendes tun:

 private static List<Integer> getAllIndexesOfSubstringInString(String fullString, String substring) {
    int pointIndex = 0;
    List<Integer> allOccurences = new ArrayList<Integer>();
    while(fullPdfText.indexOf(substring,pointIndex) >= 0){
       allOccurences.add(fullPdfText.indexOf(substring, pointIndex));
       pointIndex = fullPdfText.indexOf(substring, pointIndex) + substring.length();
    }
    return allOccurences;
}
Nashorn
quelle
0
public static int getCountSubString(String str , String sub){
int n = 0, m = 0, counter = 0, counterSub = 0;
while(n < str.length()){
  counter = 0;
  m = 0;
  while(m < sub.length() && str.charAt(n) == sub.charAt(m)){
    counter++;
    m++; n++;
  }
  if (counter == sub.length()){
    counterSub++;
    continue;
  }
  else if(counter > 0){
    continue;
  }
  n++;
}

return  counterSub;

}}

Nikolai Nechai
quelle
Diese Frage ist 8 Jahre alt und ohne einen Hinweis darauf, warum dies eine bessere Lösung als die 22 anderen veröffentlichten Lösungen ist, sollte sie wahrscheinlich entfernt werden
Jason Wheeler
0

Diese Lösung gibt die Gesamtzahl des Auftretens eines bestimmten Teilstrings in der gesamten Zeichenfolge aus und schließt auch die Fälle ein, in denen überlappende Übereinstimmungen vorhanden sind.

class SubstringMatch{
    public static void main(String []args){
        //String str = "aaaaabaabdcaa";
        //String sub = "aa";
        //String str = "caaab";
        //String sub = "aa";
        String str="abababababaabb";
        String sub = "bab";

        int n = str.length();
        int m = sub.length();

        // index=-1 in case of no match, otherwise >=0(first match position)
        int index=str.indexOf(sub), i=index+1, count=(index>=0)?1:0;
        System.out.println(i+" "+index+" "+count);

        // i will traverse up to only (m-n) position
        while(index!=-1 && i<=(n-m)){   
            index=str.substring(i, n).indexOf(sub);
            count=(index>=0)?count+1:count;
            i=i+index+1;  
            System.out.println(i+" "+index);
        }
        System.out.println("count: "+count);
    }
}
Anubhav Singh
quelle