Как получить IndexOf строки с несколькими возможными символами? - PullRequest
0 голосов
/ 18 июня 2020

Мне нужна функция, которая может получить первый индекс одного из нескольких возможных символов. Я не хочу использовать регулярное выражение из-за плохой производительности. Я попытался получить минимум двух IndexOf (s), но он не работает, когда он содержится в одной строке, а не в другой, потому что -1 меньше обоих индексов.

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

Ответы [ 3 ]

3 голосов
/ 19 июня 2020

Предлагаю немного посложнее , но надеюсь удобнее решение:

// 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)
0 голосов
/ 19 июня 2020

Если я понимаю, о чем вы спрашиваете, это получение наименьшего индекса между индексами двух символов в строке, но проблема в том, что если существует только один из них, возвращается индекс другого, потому что он -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, который принимает 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;
0 голосов
/ 18 июня 2020

Простой ответ:

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 выберет меньший индекс, если он существует.

EDIT: Если символы, с которыми вы имеете дело, являются одной и той же буквой, если разные случаи , вы можете сделать:

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