简体   繁体   English

如何查找字符串中第n次出现的字符?

[英]How to find nth occurrence of character in a string?

Similar to a question posted here , am looking for a solution in Java.此处发布的问题类似,我正在寻找 Java 中的解决方案。

That is, how to find the index of nth occurrence of a character/string from a string?即,如何从字符串中找到第n次出现的字符/字符串的索引?

Example: " /folder1/folder2/folder3/ ".示例:/folder1/folder2/folder3/ ”。 In this case, if I ask for 3rd occurrence of slash (/), it appears before folder3, and I expect to return this index position. My actual intention is to substring it from nth occurrence of a character.在这种情况下,如果我要求第 3 次出现斜杠 (/),它会出现在 folder3 之前,并且我希望返回此索引 position。我的实际意图是从第 n 个字符出现时返回 substring。

Is there any convenient/ready-to-use method available in Java API or do we need to write a small logic on our own to solve this? Java API 有没有什么方便/现成的方法,还是需要自己写个小逻辑来解决?

Also,还,

  1. I quickly searched whether any method is supported for this purpose at Apache Commons Lang's StringUtils , but I don't find any.我在 Apache Commons Lang 的StringUtils快速搜索是否支持任何方法用于此目的,但我没有找到任何方法。
  2. Can regular expressions help in this regard?正则表达式在这方面有帮助吗?

If your project already depends on Apache Commons you can use StringUtils.ordinalIndexOf , otherwise, here's an implementation:如果您的项目已经依赖于 Apache Commons,您可以使用StringUtils.ordinalIndexOf ,否则,这是一个实现:

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

This post has been rewritten as an article here .这篇文章在这里被改写为一篇文章。

I believe the easiest solution for finding the Nth occurrence of a String is to use StringUtils.ordinalIndexOf() from Apache Commons.我相信查找第 N 个字符串的最简单解决方案是使用 Apache Commons 中的StringUtils.ordinalIndexOf()

Example:例子:

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

Two simple options occur:出现两个简单的选项:

  • Use charAt() repeatedly重复使用charAt()
  • Use indexOf() repeatedly重复使用indexOf()

For example:例如:

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

That may well not perform as well as using indexOf repeatedly, but it's possibly simpler to get right.这可能不如反复使用indexOf表现得那么好,但正确使用可能更简单。

You can try something like this:你可以尝试这样的事情:

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

Note that I did some assumptions in the regex:请注意,我在正则表达式中做了一些假设:

  • the input path is absolute (ie starts with "/");输入路径是绝对的(即以“/”开头);
  • you do not need the 3rd "/" in the result.结果中不需要第三个“/”。

As requested in a comment, I'll try to explain the regex: (/[^/]*){2}/([^/]*)根据评论中的要求,我将尝试解释正则表达式: (/[^/]*){2}/([^/]*)

正则表达式可视化

  • /[^/]* is a / followed by [^/]* (any number of characters that are not a / ), /[^/]*是一个/后跟[^/]* (任意数量的不是/的字符),
  • (/[^/]*) groups the previous expression in a single entity. (/[^/]*)将前一个表达式分组到一个实体中。 This is the 1 st group of the expression,这是表达式的1组,
  • (/[^/]*){2} means that the group must match extactly {2} times, (/[^/]*){2}表示该组必须完全匹配{2}次,
  • [^/]* is again any number of characters that are not a / , [^/]*再次是任意数量的不是/的字符,
  • ([^/]*) groups the previos expression in a single entity. ([^/]*)将 previos 表达式分组到单个实体中。 This is the 2 nd group of the expression.这是2的表达的第二组。

This way you have only to get the substring that matches the 2nd group: return m.group(2);这样你只需要得到与第二组匹配的子字符串: return m.group(2);

Image courtesy by Debuggex图片由Debuggex 提供

I made a few changes to aioobe's answer and got a nth lastIndexOf version, and fix some NPE problems.我对 aioobe 的答案进行了一些更改,并获得了第 n 个 lastIndexOf 版本,并修复了一些 NPE 问题。 See code below:见下面的代码:

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;
}
 ([.^/]*/){2}[^/]*(/)

Match anything followed by / two times, then again.匹配后跟 / 两次的任何内容,然后再次匹配。 The third one is the one you want第三个就是你想要的

The Matcher state can be used to tell where the last / is Matcher状态可用于判断最后一个 / 在哪里

May be you could achieve this through String.split(..) method also.也许你也可以通过 String.split(..) 方法实现这一点。

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

Nowadays there IS support of Apache Commons Lang's StringUtils ,现在有 Apache Commons Lang 的StringUtils 支持

This is the primitive:这是原语:

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

for your problem you can code the following: StringUtils.ordinalIndexOf(uri, "/", 3)对于您的问题,您可以编写以下代码: StringUtils.ordinalIndexOf(uri, "/", 3)

You can also find the last nth occurrence of a character in a string with the lastOrdinalIndexOf method.您还可以使用lastOrdinalIndexOf方法查找字符串中最后第 n 次出现的字符。

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

Another approach:另一种方法:

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

This answer improves on @aioobe 's answer.这个答案改进了@aioobe 的答案。 Two bugs in that answer were fixed.该答案中的两个错误已修复。
1. n=0 should return -1. 1. n=0 应该返回 -1。
2. nth occurence returned -1, but it worked on n-1th occurences. 2. 第 n 次出现返回 -1,但它对第 n-1 次出现有效。

Try this !试试这个 !

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

My 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;
}
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;
        }
    }
}
/* 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;
        }
    }
}

The code returns the nth occurrence positions substring aka field width.该代码返回第 n 个出现位置的子字符串,即字段宽度。 Example.例子。 if string "Stack overflow in low melow" is the string to search 2nd occurance of token "low", you will agree with me that it 2nd occurance is at subtring "18 and 21" .如果字符串"Stack overflow in low melow"是搜索标记 "low" 的第二次出现的字符串,你会同意我的看法,它第二次出现在 subtring "18 and 21" indexOfOccurance("Stack overflow in low melow", low, 2) returns 18 and 21 in a string. indexOfOccurance("Stack overflow in low melow", low, 2) 在字符串中返回 18 和 21。

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

//scala //scala

// throw's -1 if the value isn't present for nth time, even if it is present till n-1 th time. // 如果该值第 n 次不存在,则抛出 -1,即使它一直存在到第 n-1 次。 // throw's index if the value is present for nth time // 如果该值出现第 n 次,则抛出索引

def indexOfWithNumber(tempString:String,valueString:String,numberOfOccurance:Int):Int={
var stabilizeIndex=0 
var tempSubString=tempString 
var tempIndex=tempString.indexOf(valueString) 
breakable
{
for ( i <- 1 to numberOfOccurance)
if ((tempSubString.indexOf(valueString) != -1) && (tempIndex != -1))
{
tempIndex=tempSubString.indexOf(valueString)
tempSubString=tempSubString.substring(tempIndex+1,tempSubString.size) // ADJUSTING FOR 0
stabilizeIndex=stabilizeIndex+tempIndex+1 // ADJUSTING FOR 0
}
else
{ 
stabilizeIndex= -1
tempIndex= 0
break
}
}
stabilizeIndex match { case value if value <= -1 => -1 case _ => stabilizeIndex-1 } // reverting for adjusting 0 previously
}


indexOfWithNumber("bbcfgtbgft","b",3) // 6
indexOfWithNumber("bbcfgtbgft","b",2) //1
indexOfWithNumber("bbcfgtbgft","b",4) //-1

indexOfWithNumber("bbcfgtbcgft","bc",1)  //1
indexOfWithNumber("bbcfgtbcgft","bc",4) //-1
indexOfWithNumber("bbcfgtbcgft","bc",2) //6

APACHE IMPLEMENTATION (copy-paste: don't import a whole library for one function!) APACHE 实现(复制粘贴:不要为一个函数导入整个库!)

Here is the exact Apache Commons implementation decoupled from their StringUtils library (so that you can just copy paste this and don't have to add a dependency for the library for just one function ):这是与 StringUtils 库分离的确切 Apache Commons 实现(这样您只需复制粘贴即可,而不必仅为一个函数添加库的依赖项):

/**
 * <p>Finds the n-th index within a String, handling {@code null}.
 * This method uses {@link String#indexOf(String)} if possible.</p>
 * <p>Note that matches may overlap<p>
 *
 * <p>A {@code null} CharSequence will return {@code -1}.</p>
 *
 * @param str  the CharSequence to check, may be null
 * @param searchStr  the CharSequence to find, may be null
 * @param ordinal  the n-th {@code searchStr} to find, overlapping matches are allowed.
 * @param lastIndex true if lastOrdinalIndexOf() otherwise false if ordinalIndexOf()
 * @return the n-th index of the search CharSequence,
 *  {@code -1} if no match or {@code null} string input
 */
private static int ordinalIndexOf(final String str, final String searchStr, final int ordinal, final boolean lastIndex) {
    if (str == null || searchStr == null || ordinal <= 0) {
        return -1;
    }
    if (searchStr.length() == 0) {
        return lastIndex ? str.length() : 0;
    }
    int found = 0;
    // set the initial index beyond the end of the string
    // this is to allow for the initial index decrement/increment
    int index = lastIndex ? str.length() : -1;
    do {
        if (lastIndex) {
            index = str.lastIndexOf(searchStr, index - 1); // step backwards thru string
        } else {
            index = str.indexOf(searchStr, index + 1); // step forwards through string
        }
        if (index < 0) {
            return index;
        }
        found++;
    } while (found < ordinal);
    return index;
}

It looks like the string you want to substring is a file path.看起来你想要子串的字符串是一个文件路径。 Can't you just split by / and then consume the array entries from the point of interest onward?你不能只是用/分割然后从兴趣点开始使用数组条目吗? For example,例如,

String folders = "/folder1/folder2/folder3/".split('/');
StringBuilder subStringed = new StringBuilder('/');
for (int i = 2; i < folders.length; i++) {
  subStringed.append(folders[i]).append('/').;
}
System.out.println(subStringed.toString());

static int nthOccurrenceOfChar(String str, int n, char ch) {
    int count = 0;
    for (int i = 0; i < str.length(); i++)
        if (str.charAt(i) == ch && ++count == n)
            return i;
    return -1;
}

Yes, Regular Expressions def.netly help in this regard!是的,正则表达式 def.net 在这方面很有帮助!

To get a substring of everything after the nth occurrence, use this simple one-liner:要在第 n 次出现后得到所有内容的 substring,请使用这个简单的单行代码:

public static String afterNthOccurance(String string, char ch, int n) {
    return string.replaceAll("^([^"+ch+"]*"+ch+"){"+n+"}", "");
}

For anyone who actually wants the index of the nth occurance, you can use this:对于任何真正想要第 n 次出现的索引的人,您可以使用这个:

public static int nthIndex(String string, char ch, int n) {
    return string.length()-string.replaceAll("^([^"+ch+"]*"+ch+"){"+n+"}", "").length()-1;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM