从String中的char位置获取行号的最简单方法是什么?

Ast*_*tor 8 c# string

从C#中的字符串中的char位置获取行号的最简单方法是什么?(或获取行的位置(第一个字符串))是否有内置函数?如果没有这样的功能,那么编写扩展名的好方法就像:

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) { .... }

}
Run Code Online (Sandbox Code Playgroud)

编辑:添加方法PosFromLine

Jon*_*eet 15

Jan的建议稍有不同,没有创建新的字符串:

var lineNumber = input.Take(pos).Count(c => c == '\n') + 1;
Run Code Online (Sandbox Code Playgroud)

使用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();
Run Code Online (Sandbox Code Playgroud)

认为这样可行......但我不确定我是否会写出非LINQ方法......


Jan*_*oom 12

计算子网格输入字符串中的换行符数.

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

编辑:并执行一个+1因为行号从1开始:-)


gho*_*ord 5

如果您要对同一个长字符串多次调用该函数,则此类可能很有用。它缓存新的行位置,以便稍后它可以执行 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的测试用例:

[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));
}
Run Code Online (Sandbox Code Playgroud)