获取字符串中第n个char出现的索引

时间:2012-07-06 13:26:03

标签: c# .net string

我正在尝试创建一个函数,该函数返回字符串中给定char的第N次出现的索引。

这是我的尝试:

private int IndexOfNth(string str, char c, int n)
{
    int index = str.IndexOf(c) + 1;
    if (index >= 0)
    {
        string temp = str.Substring(index, str.Length - index);
        for (int j = 1; j < n; j++)
        {
            index = temp.IndexOf(c) + 1;
            if (index < 0)
            {
                return -1;
            }
            temp = temp.Substring(index, temp.Length - index);
        }
        index = index + (str.Length);
    }
    return index;
}

但是我没有考虑最终子字符串的索引将如何偏离原始字符串中的原始实际索引。我如何使这项工作?

另外作为一个附带问题,如果我想将char作为制表符,我是否将这个函数传递给'\ t'或者什么?

9 个答案:

答案 0 :(得分:40)

不要那样做; IndexOf采用第二个参数指定从哪里开始。

private static int IndexOfNth(string str, char c, int n) {
    int s = -1;

    for (int i = 0; i < n; i++) {
        s = str.IndexOf(c, s + 1);

        if (s == -1) break;
    }

    return s;
}

答案 1 :(得分:23)

采取所有这些子串对我来说似乎很浪费。为什么不循环自己?

private int IndexOfNth(string str, char c, int n)
{
    int remaining = n;
    for (int i = 0; i < str.Length; i++)
    {
        if (str[i] == c)
        {
            remaining--;
            if (remaining == 0)
            {
                return i;
            }
        }
    }
    return -1;
}

(我考虑在像minitech的解决方案这样的循环中使用IndexOf,但认为它有点繁琐。当然,两者都很好。两者基本上都做同样的工作,只检查每个字符一次。使用{{ 1}}可能会稍微高效一些,但要找到更具可读性的那些。)

答案 2 :(得分:15)

使用LINQ查找字符串a中第5个aababaababa的索引:

var str = "aababaababa";
var ch = 'a';
var n = 5;
var result = str
  .Select((c, i) => new { c, i })
  .Where(x => x.c == ch)
  .Skip(n - 1)
  .FirstOrDefault();
return result != null ? result.i : -1;

答案 3 :(得分:6)

我倾向于首先考虑如何使用Linq访问该集合。

  // 0-based n.
char result = str
  .Where(x => x == c)
  .Skip(n)
  .FirstOrDefault();

然后我将解包linq并添加索引迭代。

int foundCount = -1;
for(int position = 0; position < str.Length; position++)
{
  char x = str[position];
  if (x == c)
  {
    foundCount += 1;
    // 0-based n
    if (foundCount == n)
    {
      return position;
    }
  }
}
return -1;

然后我想一想:如果这个方法返回所有索引,那么我可以查询它们:

public IEnumerable<int> IndexesOf(string str, char c)
{
  for(int position = 0; position < str.Length; position++)
  {
    char x = str[position];
    if (x == c)
    {
      yield return position;
    }
  }
}

被叫:

int position = IndexesOf(str, c)
 .Skip(n) // 0-based n
 .DefaultIfEmpty(-1)
 .First();

答案 4 :(得分:1)

为什么不使用the IndexOf overload that takes a starting index而不是创建一堆子串呢?这将更容易(您不必调整最终索引)和更高效(您不必分配一堆子串)。

答案 5 :(得分:1)

未经测试,但这样的事情应该有效:

private int IndexOfNth(string str, char c, int n)
{
    int index = -1;
    while (n-- > 0)
    {
        index = str.IndexOf(c, index + 1);
        if (index == -1) break;
    }
    return index;
}

答案 6 :(得分:0)

还没见过有人使用CharEnumerator ......

    public Int32 getNthIndex(string str, char c, Int32 n)
    {
        Int32 index = 0;
        Int32 count = 0;
        if (str != null && str.Length > 0 && !(n < 1))
        {
            CharEnumerator scanner = str.GetEnumerator();
            while (scanner.MoveNext())
            {
                if (scanner.Current == c) { count++; }
                if (count == n) { break; }
                index++;
            }
            if (count < n) { index = -1; }
        }
        if (count == 0) { return -1; } else { return index; }
    }

应该非常高效,没有子串或任何东西,只需扫描你给出的字符串并保持计数。

答案 7 :(得分:0)

您可以使用以下方法返回指定字符串中第n次出现的指定字符。

public static int IndexOfNthCharacter(string str, int n, char c) {
    int index = -1;
    if (!str.Contains(c.ToString()) || (str.Split(c).Length-1 < n)) {
        return -1;
    }
    else {
        for (int i = 0; i < str.Length; i++) {
            if (n > 0) {            
                index++;
            }
            else {
                return index;
            }
            if (str[i] == c) {
                n--;
            }
        }
        return index;
    }
}

请注意,如果您要搜索的字符串中不存在您要搜索的字符,或者您要搜索的事件编号大于字符串中存在的编号,则此方法将返回-1。

答案 8 :(得分:0)

首先,我将它作为一种扩展方法。这样,您可以跳过其他强制性null检查,也可以像使用IndexOfIndexOfAny等一样在字符串上调用它。

然后我会制作两种方法。一个用于检索所有索引(IndexesOf,可能在某个时候派上用场)而另一个索引(IndexOfNth)使用第一个函数来检查第n个索引:

using System;
using System.Collections.Generic; // # Necessary for IList<int>
using System.Linq; // # Necessary for IList<int>.ToArray()

/// <summary>
/// Returns all indexes of the specified <paramref name="value"/> in the current string.
/// </summary>
/// <param name="@this">The current string this method is operating on.</param>
/// <param name="value">The value to be searched.</param>
/// <returns><c>Null</c>, if <paramref name="value"/> is <c>null</c> or empty.
/// An array holding all indexes of <paramref name="value"/> in this string,
/// else.</returns>
static int[] IndexesOf(this string @this, string value)
{
    // # Can't search for null or string.Empty, you can return what
    //   suits you best
    if (string.IsNullOrEmpty(value))
        return null;

    // # Using a list instead of an array saves us statements to resize the 
    //   array by ourselves
    IList<int> indexes = new List<int>();
    int startIndex = 0;

    while (startIndex < @this.Length)
    {
        startIndex = @this.IndexOf(value, startIndex);
        if (startIndex >= 0)
        {
            // # Add the found index to the result and increment it by length of value
            //   afterwards to keep searching AFTER the current position
            indexes.Add(startIndex);
            startIndex += value.Length;
        }
        else
        {
            // # Exit loop, if value does not occur in the remaining string
            break;
        }
    }

    // # Return an array to conform with other string operations.
    return indexes.ToArray();
}

/// <summary>
/// Returns the indexes of the <paramref name="n"/>th occurrence of the specified
/// <paramref name="value"/> in the current string.
/// </summary>
/// <param name="@this">The current string this method is operating on.</param>
/// <param name="value">The value to be searched.</param>
/// <param name="n">The 1-based nth occurrence.</param>
/// <returns><c>-1</c>, if <paramref name="value"/> is <c>null</c> or empty -or-
/// <paramref name="n"/> is less than 1.</returns>
static int IndexOfNth(this string @this, string value, int n /* n is 1-based */)
{
    // # You could throw an ArgumentException as well, if n is less than 1
    if (string.IsNullOrEmpty(value) || n < 1)
        return -1;

    int[] indexes = @this.IndexesOf(value);

    // # If there are n or more occurrences of 'value' in '@this'
    //   return the nth index.
    if (indexes != null && indexes.Length >= n)
    {
        return indexes[n - 1];
    }

    return -1;
}

您可以在签名中使用char value代替string value来重载这些内容,并通过value.ToString()调用各自的对应方。 Etvoilá!

当然,这些方法可以重构,例如使用LINQ,使IndexesOf递归等。