С# третий индекс символа в строке

есть ли команда, которая может получить третий индекс символа в строке? Например:

error: file.ext: line 10: invalid command [test:)]

В приведенном выше предложении я хочу указать индекс третьего двоеточия, следующий рядом с 10. Как я могу это сделать? Я знаю string.IndexOf и string.LastIndexOf, но в этом случае я хочу получить индекс символа, когда он используется в третий раз.

Ответ 1

String.IndexOf предоставит вам индекс первого, но имеет перегрузки, дающие начальную точку. Таким образом, вы можете использовать результат первого IndexOf плюс один в качестве отправной точки для следующего. А затем просто накапливайте индексы достаточное количество раз:

var offset = myString.IndexOf(':');
offset = myString.IndexOf(':', offset+1);
var result = myString.IndexOf(':', offset+1);

Добавьте обработку ошибок, если вы не знаете, что myString содержит не менее трех двоеточий.

Ответ 2

Вы можете написать что-то вроде:

    public static int CustomIndexOf(this string source, char toFind, int position)
    {
        int index = -1;
        for (int i = 0; i < position; i++)
        {
            index = source.IndexOf(toFind, index + 1);

            if (index == -1)
                break;
        }

        return index;
    }

РЕДАКТИРОВАТЬ. Очевидно, вы должны использовать его следующим образом:

int colonPosition = myString.CustomIndexOf(',', 3);

Ответ 3

Я предполагаю, что вы хотите разобрать эту строку на разные части.

public static void Main() {
    var input = @"error: file.ext: line 10: invalid command [test (: ]";
    var splitted = input .Split(separator: new[] {": "}, count: 4, options: StringSplitOptions.None);

    var severity = splitted[0]; // "error"
    var filename = splitted[1]; // "file.ext"
    var line = splitted[2];     // "line 10"
    var message = splitted[3];  // "invalid command [test (: ]"
}

Ответ 4

На это уже были ответы несколько очень хороших способов, но я решил попробовать и написать его с помощью Expressions.

private int? GetNthOccurrance(string inputString, char charToFind, int occurranceToFind)
{
    int totalOccurrances = inputString.ToCharArray().Count(c => c == charToFind);
    if (totalOccurrances < occurranceToFind || occurranceToFind <= 0)
    {
        return null;
    }

    var charIndex =
        Enumerable.Range(0, inputString.Length - 1)
            .Select(r => new { Position = r, Char = inputString[r], Count = 1 })
            .Where(r => r.Char == charToFind);


    return charIndex
        .Select(c => new
        {
            c.Position,
            c.Char,
            Count = charIndex.Count(c2 => c2.Position <= c.Position)
        })
        .Where(r => r.Count == occurranceToFind)
        .Select(r => r.Position)
        .First();
}

и тесты для подтверждения:

Assert.AreEqual(0, GetNthOccurrance(input, 'h', 1)); 
Assert.AreEqual(3, GetNthOccurrance(input, 'l', 2));
Assert.IsNull(GetNthOccurrance(input, 'z', 1));
Assert.IsNull(GetNthOccurrance(input, 'h', 10));

Ответ 5

Вы можете вызвать .IndexOf(char, position) для поиска из желаемой позиции, поэтому вы должны называть его 3 раза (но после каждого вызова вы также должны проверить, что-то найдено).

Ответ 6

int pos = -1;
for ( int k = 0; k < 3; ++k )
{
  pos = s.indexOf( ':', pos+1 );
  // Check if pos < 0...
}

Ответ 7

Немного уродливый, но альтернативный подход (к другим уже опубликованным), который работает:

public int FindThirdColonIndex(string msg)
{       
    for (int i = 0, colonCount = 0; i < msg.Length; i++)
    {
        if (msg[i] == ':' && ++colonCount == 3) { return i; }
    }

    // Not found
    return -1;
}

Ответ 8

Вот рекурсивная реализация (для string not char) - как метод расширения, который искажает формат метода (ов) структуры.

Все, что вам нужно сделать, это изменить "строковое значение" на "char значение" в методе расширения и соответствующим образом обновить тесты, и это сработает... Я счастлив сделать это и опубликовать его, если кто-либо интересно?

public static int IndexOfNth(
    this string input, string value, int startIndex, int nth)
{
    if (nth < 1)
        throw new NotSupportedException("Param 'nth' must be greater than 0!");
    if (nth == 1)
        input.IndexOf(value, startIndex);
    return
        input.IndexOfNth(value, input.IndexOf(value, startIndex) + 1, --nth);
}

Кроме того, вот некоторые (MBUnit) модульные тесты, которые могут вам помочь (чтобы убедиться, что это правильно):

[Test]
public void TestIndexOfNthWorksForNth1()
{
    const string input = "foo<br />bar<br />baz<br />";
    Assert.AreEqual(3, input.IndexOfNth("<br />", 0, 1));
}

[Test]
public void TestIndexOfNthWorksForNth2()
{
    const string input = "foo<br />whatthedeuce<br />kthxbai<br />";
    Assert.AreEqual(21, input.IndexOfNth("<br />", 0, 2));
}

[Test]
public void TestIndexOfNthWorksForNth3()
{
    const string input = "foo<br />whatthedeuce<br />kthxbai<br />";
    Assert.AreEqual(34, input.IndexOfNth("<br />", 0, 3));
}

Ответ 9

Пожалуйста, смотрите этот ответ по аналогичному вопросу: fooobar.com/info/305921/...
Он предоставляет метод для поиска индекса n-го вхождения определенного символа в указанной строке.

В вашем конкретном случае это будет реализовано так:

int index = IndexOfNthCharacter("error: file.ext: line 10: invalid command [test:)]", 3, ':');