【问题标题】:How do you get IndexOf of a string with multiple possible chars?如何获得具有多个可能字符的字符串的 IndexOf?
【发布时间】:2020-06-18 20:47:40
【问题描述】:

我需要一个可以获取多个可能字符之一的第一个索引的函数。由于性能不佳,我不想使用正则表达式。我尝试获取两个 IndexOf(s) 的最小值,但是当它包含在一个字符串而不是另一个字符串中时它不起作用,因为 -1 小于两个索引。

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

【问题讨论】:

  • 我发现你可以使用 IndexOfAny。

标签: c# indexof


【解决方案1】:

我建议有点复杂的,但我希望更方便解决方案:

// 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】:

如果我明白你在问什么,它是获取字符串中两个字符的索引之间的最小索引,但问题是如果只有一个存在,则返回另一个的索引,因为它是 @ 987654322@.

解决这个问题的一种方法是在第一个字符串中测试-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 方法的native overload 隐藏,该方法采用charint,返回“指定字符第一次出现的从零开始的索引,从指定位置开始。”

我相信这是因为在评估任何扩展方法之前评估和选择了本机方法(如果存在隐式匹配),但我可能错了。

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

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;

【讨论】:

    【解决方案3】:

    简单回答:

    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,-1 等效于 uint.MaxValue 这意味着这被认为是可能的最高值,这意味着如果存在 min 将选择一个较小的索引。

    编辑:如果您正在处理的字符是相同的字母,如果不同的情况下,您可以这样做:

    using System;
    public static int IndexOf (this string s, char a) => s.IndexOf(a, StringComparison.OrdinalIgnoreCase); 
    

    【讨论】:

    • 所以听起来如果两个字符都不存在,这个方法会返回uint.MaxValue而不是-1,对吗?
    • @RufusL,因为它被强制转换回 int,所以它会返回 -1。
    猜你喜欢
    • 2023-01-07
    • 2013-05-21
    • 1970-01-01
    • 2021-01-04
    • 2020-01-25
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 2016-12-26
    相关资源
    最近更新 更多