17

我正在尝试创建一个函数,该函数返回字符串中给定字符第 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;
}

应该找到第一次出现,砍掉字符串的前面部分,从新的子字符串中找到第一次出现,然后一直到它获得第 n 次出现的索引。但是我没有考虑最终子字符串的索引将如何从原始字符串中的原始实际索引偏移。我该如何进行这项工作?

另外作为一个附带问题,如果我希望 char 成为制表符,我应该传递这个函数 '\t' 还是什么?

4

9 回答 9

43

不要那样做;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;
}
于 2012-07-06T13:29:27.407 回答
26

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

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

(我考虑IndexOf过像 minitech 的解决方案一样在循环中使用,但认为它有点繁琐。当然,两者都可以。两者基本上都做相同的工作,只检查每个字符一次。使用IndexOf可能会稍微更有效率,但选择哪个你会发现更具可读性。)

于 2012-07-06T13:29:03.790 回答
14

使用 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;
于 2012-07-06T13:33:55.977 回答
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();
于 2012-07-06T13:55:37.600 回答
1

为什么不使用带有起始索引IndexOf重载,而不是创建一堆子字符串?这将更容易(您不必调整最终索引)和更高效(您不必分配一堆子字符串)。

于 2012-07-06T13:28:50.210 回答
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;
}
于 2012-07-06T13:34:20.663 回答
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; }
    }

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

于 2012-07-06T18:46:24.747 回答
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。

于 2017-09-28T02:35:25.300 回答
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(). 瞧!

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

于 2018-05-29T14:21:28.487 回答