Подтвердить что ты не робот

Получить индекс первого небелосового символа в С# String

Есть ли способ получить индекс первого символа без пробелов в строке (или, более широко, индекс первого символа, соответствующего условию) в С#, не записывая собственный код цикла?

ИЗМЕНИТЬ

Под "написанием собственного кода цикла" я действительно хотел, чтобы я искал компактное выражение, которое решает проблему, не загромождая логику, над которой я работаю.

Извиняюсь за любую путаницу в этой точке.

4b9b3361

Ответ 1

Мне нравится определять собственный метод расширения для возврата индекса первого элемента, который удовлетворяет пользовательскому предикату в последовательности.

/// <summary>
/// Returns the index of the first element in the sequence 
/// that satisfies a condition.
/// </summary>
/// <typeparam name="TSource">
/// The type of the elements of <paramref name="source"/>.
/// </typeparam>
/// <param name="source">
/// An <see cref="IEnumerable{T}"/> that contains
/// the elements to apply the predicate to.
/// </param>
/// <param name="predicate">
/// A function to test each element for a condition.
/// </param>
/// <returns>
/// The zero-based index position of the first element of <paramref name="source"/>
/// for which <paramref name="predicate"/> returns <see langword="true"/>;
/// or -1 if <paramref name="source"/> is empty
/// or no element satisfies the condition.
/// </returns>
public static int IndexOf<TSource>(this IEnumerable<TSource> source, 
    Func<TSource, bool> predicate)
{
    int i = 0;

    foreach (TSource element in source)
    {
        if (predicate(element))
            return i;

        i++;
    }

    return -1;
}

Затем вы можете использовать LINQ для устранения исходной проблемы:

string str = "   Hello World";
int i = str.IndexOf<char>(c => !char.IsWhiteSpace(c));

Ответ 2

A string - это, конечно, IEnumerable<char>, поэтому вы можете использовать Linq:

int offset = someString.TakeWhile(c => char.IsWhiteSpace(c)).Count();

Ответ 3

string s= "   \t  Test";
Array.FindIndex(s.ToCharArray(), x => !char.IsWhiteSpace(x));

возвращает 6

Чтобы добавить условие, просто выполните...

Array.FindIndex(s.ToCharArray(), x => !char.IsWhiteSpace(x) && your condition);

Ответ 4

Вы можете использовать функцию String.IndexOfAny, которая возвращает первое вхождение любого символа в указанном массиве символов Юникода.

В качестве альтернативы вы можете использовать функцию String.TrimStart, которая удаляет все символы пробела с начала строки. Индекс первого небелого пробела - это разница между длиной исходной строки и обрезанной.

Вы даже можете выбрать набор символов для обрезки:)

В принципе, если вы ищете ограниченный набор символов (пусть говорят цифры), вы должны пойти первым методом.

Если вы пытаетесь игнорировать ограниченный набор символов (например, пробелы), вы должны пойти со вторым методом.

Последний метод заключается в использовании методов Linq:

string s = "        qsdmlkqmlsdkm";
Console.WriteLine(s.TrimStart());
Console.WriteLine(s.Length - s.TrimStart().Length);
Console.WriteLine(s.FirstOrDefault(c => !Char.IsWhiteSpace(c)));
Console.WriteLine(s.IndexOf(s.FirstOrDefault(c => !Char.IsWhiteSpace(c))));

Вывод:

qsdmlkqmlsdkm
8
q
8

Ответ 5

var match = Regex.Match(" \t test  ", @"\S"); // \S means all characters that are not whitespace
if (match.Success)
{
    int index = match.Index;
    //do something with index
}
else
{
    //there were no non-whitespace characters, handle appropriately
}

Если вы будете делать это часто, по соображениям производительности вам следует кэшировать скомпилированный Regex для этого шаблона, например:

static readonly Regex nonWhitespace = new Regex(@"\S");

Затем используйте его как:

nonWhitespace.Match(" \t test  ");

Ответ 6

Вы можете обрезать, получить первый символ и использовать IndexOf.

Ответ 7

Поскольку здесь было несколько решений, я решил сделать некоторые тесты производительности, чтобы увидеть, как они работают. Решили поделиться этими результатами для заинтересованных...

    int iterations = 1000000;
    int result = 0;
    string s= "   \t  Test";

    System.Diagnostics.Stopwatch watch = new Stopwatch();

    // Convert to char array and use FindIndex
    watch.Start();
    for (int i = 0; i < iterations; i++)
        result = Array.FindIndex(s.ToCharArray(), x => !char.IsWhiteSpace(x)); 
    watch.Stop();
    Console.WriteLine("Convert to char array and use FindIndex: " + watch.ElapsedMilliseconds);

    // Trim spaces and get index of first character
    watch.Restart();
    for (int i = 0; i < iterations; i++)
        result = s.IndexOf(s.TrimStart().Substring(0,1));
    watch.Stop();
    Console.WriteLine("Trim spaces and get index of first character: " + watch.ElapsedMilliseconds);

    // Use extension method
    watch.Restart();
    for (int i = 0; i < iterations; i++)
        result = s.IndexOf<char>(c => !char.IsWhiteSpace(c));
    watch.Stop();
    Console.WriteLine("Use extension method: " + watch.ElapsedMilliseconds);

    // Loop
    watch.Restart();
    for (int i = 0; i < iterations; i++)
    {   
        result = 0;
        foreach (char c in s)
        {
            if (!char.IsWhiteSpace(c))
                break;
            result++;
        }
    }
    watch.Stop();
    Console.WriteLine("Loop: " + watch.ElapsedMilliseconds);

Результаты приведены в миллисекундах....

Где s = "\ t Test"
Преобразуйте в массив char и используйте FindIndex: 154
Обрезать пробелы и получить индекс первого символа: 189
Используйте метод расширения: 234
Петля: 146

Где s = "Тест"
Преобразуйте в массив char и используйте FindIndex: 39
Обрезать пробелы и получить индекс первого символа: 155
Используйте метод расширения: 57
Петля: 15

Где s = (1000 символов без пробелов)
Преобразуйте в массив char и используйте FindIndex: 506
Обрезать пробелы и получить индекс первого символа: 534
Используйте метод расширения: 51
Петля: 15

Где s = (строка с 1000 символами, начинающаяся с "\ t Test" )
Преобразуйте в массив char и используйте FindIndex: 609
Вырезать пробелы и получить индекс первого символа: 1103
Используйте метод расширения: 226
Петля: 146

Сделайте свои собственные выводы, но мой вывод состоит в том, чтобы использовать тот, который вам нравится больше всего, потому что различия в производительности незначительны в реального мира.

Ответ 8

Существует очень простое решение

string test = "    hello world";
int pos = test.ToList<char>().FindIndex(x => char.IsWhiteSpace(x) == false);

pos будет 4

у вас могут быть более сложные условия, например:

pos = test.ToList<char>().FindIndex((x) =>
                {
                    if (x == 's') //Your complex conditions go here
                        return true;
                    else 
                        return false;
                }
            );

Ответ 9

Да, вы можете попробовать следующее:

string stg = "   xyz";
int indx = (stg.Length - stg.Trim().Length);  

Ответ 10

Что-то собирается зацикливаться где-то. Для полного контроля над тем, что есть и не является пробелом, вы можете использовать linq для объектов, чтобы сделать ваш цикл:

int index = Array.FindIndex(
               s.ToCharArray(), 
               x => !(new [] { '\t', '\r', '\n', ' '}.Any(c => c == x)));

Ответ 11

Здесь много решений, которые преобразуют строку в массив. Это необязательно, отдельные символы в строке могут быть доступны как элементы в массиве.

Это мое решение, которое должно быть очень эффективным:

private static int FirstNonMatch(string s, Func<char, bool> predicate, int startPosition = 0)
{
    for (var i = startPosition; i < s.Length; i++)
        if (!predicate(s[i])) return i;

    return -1;
}

private static int LastNonMatch(string s, Func<char, bool> predicate, int startPosition)
{
    for (var i = startPosition; i >= 0; i--)
        if (!predicate(s[i])) return i;

    return -1;
}

И чтобы использовать их, сделайте следующее:

var x = FirstNonMatch(" asdf ", char.IsWhiteSpace);
var y = LastNonMatch(" asdf ", char.IsWhiteSpace, " asdf ".Length);