Какой самый простой способ получить номер строки из позиции символа в строке? - PullRequest
7 голосов
/ 31 августа 2011

Какой самый простой способ получить номер строки из позиции символа в строке в C #? (или получить Положение строки (первый символ в строке)) Есть ли встроенная функция? Если такой функции нет, лучше написать расширение, например:

public static class StringExt {
    public static int LineFromPos(this String S, int Pos) { 
        int Res = 1;
        for (int i = 0; i <= Pos - 1; i++)
            if (S[i] == '\n') Res++;
        return Res;                
    }

    public static int PosFromLine(this String S, int Pos) { .... }

}

Отредактировано: добавлен метод PosFromLine

Ответы [ 3 ]

15 голосов
/ 31 августа 2011

Небольшое изменение по предложению Яна, без создания новой строки:

var lineNumber = input.Take(pos).Count(c => c == '\n') + 1;

Использование Take ограничивает размер ввода без необходимости копировать строковые данные.

Вы должны учитывать, каким должен быть результат, если данный символ является переводом строки, кстати ... а также хотите ли вы обрабатывать "foo\rbar\rbaz" как три строки.

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

var pos = input.Select((value, index) => new { value, index })
               .Where(pair => pair.value == '\n')
               .Select(pair => pair.index + 1)
               .Take(line - 1)
               .DefaultIfEmpty(1) // Handle line = 1
               .Last();

Я думаю, что это сработает ... но я не уверен, что я бы просто не выписал подход без LINQ ...

10 голосов
/ 31 августа 2011

Подсчитать количество новых строк во входной строке с подстрокой.

var lineNumber = input.Substring(0, pos).Count(c=>c == '\n') + 1;

edit : и сделать +1, так как номера строк начинаются с 1: -)

2 голосов
/ 07 марта 2016

Если вы собираетесь вызывать функцию много раз в одной и той же длинной строке, этот класс может быть полезен. Он кэширует позиции новой строки, так что позже он может выполнить поиск O (log (разрывы строк в строке)) для GetLine и O (1) для GetOffset.

public class LineBreakCounter
{
    List<int> lineBreaks_ = new List<int>();
    int length_;

    public LineBreakCounter(string text)
    {
        if (text == null)
            throw new ArgumentNullException(nameof(text));

        length_ = text.Length;
        for (int i = 0; i < text.Length; i++)
        {
            if (text[i] == '\n')
                lineBreaks_.Add(i);

            else if (text[i] == '\r' && i < text.Length - 1 && text[i + 1] == '\n')
                lineBreaks_.Add(++i);
        }
    }

    public int GetLine(int offset)
    {
        if (offset < 0 || offset > length_)
            throw new ArgumentOutOfRangeException(nameof(offset));

        var result = lineBreaks_.BinarySearch(offset);
        if (result < 0)
            return ~result;
        else
            return result;
    }

    public int Lines => lineBreaks_.Count + 1;

    public int GetOffset(int line)
    {
        if (line < 0 || line >= Lines)
            throw new ArgumentOutOfRangeException(nameof(line));

        if (line == 0)
            return 0;

        return lineBreaks_[line - 1] + 1;
    }
}

Вот мой тестовый пример:

[TestMethod]
public void LineBreakCounter_ShouldFindLineBreaks()
{
    var text = "Hello\nWorld!\r\n";
    var counter = new LineBreakCounter(text);

    Assert.AreEqual(0, counter.GetLine(0));
    Assert.AreEqual(0, counter.GetLine(3));
    Assert.AreEqual(0, counter.GetLine(5));
    Assert.AreEqual(1, counter.GetLine(6));
    Assert.AreEqual(1, counter.GetLine(8));
    Assert.AreEqual(1, counter.GetLine(12));
    Assert.AreEqual(1, counter.GetLine(13));
    Assert.AreEqual(2, counter.GetLine(14));

    Assert.AreEqual(3, counter.Lines);
    Assert.AreEqual(0, counter.GetOffset(0));
    Assert.AreEqual(6, counter.GetOffset(1));
    Assert.AreEqual(14, counter.GetOffset(2));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...