如何获得具有多个可能字符的字符串的 IndexOf?
How do you get IndexOf of a string with multiple possible chars?
我需要一个可以获取多个可能字符之一的第一个索引的函数。由于性能不佳,我不想使用正则表达式。我尝试获取两个 IndexOf(s) 的最小值,但是当它包含在一个字符串而不是另一个字符串中时它不起作用,因为 -1 小于两个索引。
public static int IndexOf (this string s, char a, char b) =>
Math.Min(s.IndexOf(a), s.IndexOf(b));
简单回答:
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);
如果我明白你问的是什么,它是获取字符串中两个字符的索引之间的最小索引,但问题是如果只有一个存在,则返回另一个的索引,因为这是 -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));
但是这个扩展方法有问题!!
因为存在从 char
到 int
的隐式转换,此方法将被 IndexOf
方法的 native overload 隐藏,该方法采用 char
和一个 int
,其中 returns "the zero-based index of the first occurrence of the specified character, starting at the specified position."
我认为这是因为在评估任何扩展方法之前先评估和选择本地方法(如果存在隐式匹配),但我可能错了。
为了绕过这个问题,我们可以简单地给这个方法一个不同的名字:
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. 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)
我需要一个可以获取多个可能字符之一的第一个索引的函数。由于性能不佳,我不想使用正则表达式。我尝试获取两个 IndexOf(s) 的最小值,但是当它包含在一个字符串而不是另一个字符串中时它不起作用,因为 -1 小于两个索引。
public static int IndexOf (this string s, char a, char b) =>
Math.Min(s.IndexOf(a), s.IndexOf(b));
简单回答:
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);
如果我明白你问的是什么,它是获取字符串中两个字符的索引之间的最小索引,但问题是如果只有一个存在,则返回另一个的索引,因为这是 -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));
但是这个扩展方法有问题!!
因为存在从 char
到 int
的隐式转换,此方法将被 IndexOf
方法的 native overload 隐藏,该方法采用 char
和一个 int
,其中 returns "the zero-based index of the first occurrence of the specified character, starting at the specified position."
我认为这是因为在评估任何扩展方法之前先评估和选择本地方法(如果存在隐式匹配),但我可能错了。
为了绕过这个问题,我们可以简单地给这个方法一个不同的名字:
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. 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)