如何获取包含多个可能字符的字符串的IndexOf?

3
我需要一个函数,可以获取多个可能字符中的第一个索引。我不想使用正则表达式,因为性能不佳。我尝试获取两个IndexOf(s)的最小值,但当它包含在一个字符串中而不是另一个字符串中时,它不起作用,因为-1比两个索引都要小。
public static int IndexOf (this string s, char a, char b) => 
    Math.Min(s.IndexOf(a), s.IndexOf(b));

2
我发现你可以使用IndexOfAny。 - trinalbadger587
3个回答

4

我建议一个稍微更为复杂,但我希望更加方便的解决方案:

// 1. Let's return not only index, but the char found as well
// 2. Let's accept arbitrary number of characters
// 3. Let's not interfere with existing IndexOf, IndexOfAny methods : IndexOfAnyChar
public static (int index, char value) IndexOfAnyChar(this string s, params char[] toFind) {
  //DONE: input parameters validation
  if (null == s)
    return (-1, default(char)); // or throw ArgumentNullException(nameof(s))
  else if (null == toFind || toFind.Length <= 0)
    return (-1, default(char)); // or throw ArgumentNullException(nameof(toFind))

  int bestIndex = -1;
  char bestChar = default(char);

  foreach (char c in toFind) {
    // for the long strings let's provide count for efficency
    int index = s.IndexOf(c, 0, bestIndex < 0 ? s.Length : bestIndex);

    if (index >= 0) {
      bestIndex = index;
      bestChar = c;
    }
  }

  return (bestIndex, bestChar);
}

示例:

var result = "abcde".IndexOfAnyChar('e', 'z', 'd');

// to get index only:
// int index = "abcde".IndexOfAnyChar('e', 'z', 'd').index; 

Console.Write(result);

结果:

(3, d)

2
很棒的回答。这个解决方案在性能方面比其他方案优秀得多。最好使用它。 - pneuma

1
如果我理解你的问题,那就是要在字符串中两个字符的索引之间获取最小的索引,但问题在于如果只有其中一个存在,那么返回另一个的索引,因为它是-1
解决这个问题的一种方法是在第一个字符串中测试-1,然后决定如何处理第二个字符串:
public static int IndexOf (this string s, char a, char b) => s.IndexOf(a) == -1
    // If it's not in 'a', return its index in 'b'
    ? s.IndexOf(b)                               
    : s.IndexOf(b) == -1       
        // Else if it's not in 'b', return its index in 'a'              
        ? s.IndexOf(a)                    
        // Otherwise, return the smallest index between 'a' and 'b'       
        : Math.Min(s.IndexOf(a), s.IndexOf(b));  

然而,这个扩展方法存在问题!!

由于从charint有一个隐式转换,这个方法将被IndexOf本地重载隐藏,该方法接受一个char和一个int,返回"指定字符在指定位置之后第一次出现的零基索引。"

我认为这是因为本地方法在任何扩展方法被评估之前都会被评估和选择(如果有隐式匹配),但我可能错了。

为了解决这个问题,我们可以简单地给这个方法取一个不同的名字:

public static int IndexOfFirst (this string s, char a, char b) => s.IndexOf(a) == -1
    ? s.IndexOf(b)                               
    : s.IndexOf(b) == -1       
        ? s.IndexOf(a)                    
        : Math.Min(s.IndexOf(a), s.IndexOf(b)); 

此外,我们可以利用一个params参数来让这个方法处理从中找到第一个索引的0到多个字符。
public static int IndexOfFirst(this string s, params char[] args) =>
    (args?.Any(arg => s.IndexOf(arg) > -1)).GetValueOrDefault()
        ? args.Select(arg => s.IndexOf(arg))
              .Where(index => index > -1)
              .Min()
        : -1;

-1

简单的回答:

using System;
public static int IndexOf (this string s, char a, char b) => unchecked((int)Math.Min((uint)s.IndexOf(a), (uint)s.IndexOf(b))); 

或者获取更多参数:

using System.Linq;
public static int IndexOf (this string s, params char[] arr) => unchecked((int)arr.Min(i => (uint)s.IndexOf(i)));

这个可以工作是因为在未经检查的设置中,-1 作为 uint 相当于 uint.MaxValue,这意味着它被认为是最大可能值,这意味着如果存在较小的索引,则min会选择较小的索引。
编辑:如果你处理的字符是相同字母不同大小写,你可以这样做:
using System;
public static int IndexOf (this string s, char a) => s.IndexOf(a, StringComparison.OrdinalIgnoreCase); 

1
那么,如果两个字符都不存在,这种方法会返回uint.MaxValue而不是-1,对吗? - Rufus L
@RufusL,由于它被转换为int,它将返回-1。 - trinalbadger587

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接