如何知道文本文件中streamreader的位置(linenumber)?

Pet*_*ter 9 c# streamreader

一个例子(可能不是现实生活,但要说明我的观点):

public void StreamInfo(StreamReader p)
{
    string info = string.Format(
        "The supplied streamreaer read : {0}\n at line {1}",
        p.ReadLine(),
        p.GetLinePosition()-1);               

}
Run Code Online (Sandbox Code Playgroud)

GetLinePosition这是streamreader的虚构扩展方法.这可能吗?

当然,我可以自己计算,但这不是问题.

Eam*_*mon 21

我在寻找类似问题的解决方案的同时,我需要寻找StreamReader到特定的行.我最终创建了两个扩展方法来获取和设置StreamReader上的位置.它实际上并没有提供行号计数,但实际上,我只是抓住每个ReadLine()之前的位置,如果该行感兴趣,那么我保留起始位置以便稍后设置回到这样的行:

var index = streamReader.GetPosition();
var line1 = streamReader.ReadLine();

streamReader.SetPosition(index);
var line2 = streamReader.ReadLine();

Assert.AreEqual(line1, line2);
Run Code Online (Sandbox Code Playgroud)

而重要的部分:

public static class StreamReaderExtensions
{
    readonly static FieldInfo charPosField = typeof(StreamReader).GetField("charPos", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | BindingFlags.DeclaredOnly);
    readonly static FieldInfo byteLenField = typeof(StreamReader).GetField("byteLen", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | BindingFlags.DeclaredOnly);
    readonly static FieldInfo charBufferField = typeof(StreamReader).GetField("charBuffer", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | BindingFlags.DeclaredOnly);

    public static long GetPosition(this StreamReader reader)
    {
        //shift position back from BaseStream.Position by the number of bytes read
        //into internal buffer.
        int byteLen = (int)byteLenField.GetValue(reader);
        var position = reader.BaseStream.Position - byteLen;

        //if we have consumed chars from the buffer we need to calculate how many
        //bytes they represent in the current encoding and add that to the position.
        int charPos = (int)charPosField.GetValue(reader);
        if (charPos > 0)
        {
            var charBuffer = (char[])charBufferField.GetValue(reader);
            var encoding = reader.CurrentEncoding;
            var bytesConsumed = encoding.GetBytes(charBuffer, 0, charPos).Length;
            position += bytesConsumed;
        }

        return position;
    }

    public static void SetPosition(this StreamReader reader, long position)
    {
        reader.DiscardBufferedData();
        reader.BaseStream.Seek(position, SeekOrigin.Begin);
    }
}
Run Code Online (Sandbox Code Playgroud)

这对我来说效果很好,取决于你使用反射的容忍度它认为这是一个相当简单的解决方案.

注意事项:

  1. 虽然我使用各种System.Text.Encoding选项进行了一些简单的测试,但我使用的所有数据都是简单的文本文件(ASCII).
  2. 我只使用StreamReader.ReadLine()方法,虽然简要回顾StreamReader的源代码似乎表明这在使用其他读取方法时仍然有效,但我还没有真正测试过这种情况.

  • 您应该在该字段名称之前添加下划线。[Net Core源代码](https://source.dot.net/#System.Private.CoreLib/StreamReader.cs,b5fe1efcec14de32)。 (2认同)

Ada*_*son 11

不,不太可能."行号"的概念基于已经读取的实际数据,而不仅仅是位置.例如,如果您要将读者Seek()移到任意位置,那么它不会实际读取该数据,因此无法确定行号.

唯一的方法是自己跟踪它.


小智 7

为任何TextReader提供行计数包装非常容易:

public class PositioningReader : TextReader {
    private TextReader _inner;
    public PositioningReader(TextReader inner) {
        _inner = inner;
    }
    public override void Close() {
        _inner.Close();
    }
    public override int Peek() {
        return _inner.Peek();
    }
    public override int Read() {
        var c = _inner.Read();
        if (c >= 0)
            AdvancePosition((Char)c);
        return c;
    }

    private int _linePos = 0;
    public int LinePos { get { return _linePos; } }

    private int _charPos = 0;
    public int CharPos { get { return _charPos; } }

    private int _matched = 0;
    private void AdvancePosition(Char c) {
        if (Environment.NewLine[_matched] == c) {
            _matched++;
            if (_matched == Environment.NewLine.Length) {
                _linePos++;
                _charPos = 0;
                _matched = 0;
            }
        }
        else {
            _matched = 0;
            _charPos++;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

缺点(为简洁起见):

  1. 不检查null的构造函数参数
  2. 无法识别终止线路的其他方法.读取由raw\r或\n分隔的文件时,与ReadLine()行为不一致.
  3. 不会覆盖"块"级方法,如Read(char [],int,int),ReadBlock,ReadLine,ReadToEnd.TextReader实现正常工作,因为它将其他所有内容路由到Read(); 但是,可以通过实现更好的性能
    • 通过将调用路由到_inner来覆盖这些方法.而不是基地.
    • 将读取的字符传递给AdvancePosition.请参阅示例ReadBlock实现:

public override int ReadBlock(char[] buffer, int index, int count) {
    var readCount = _inner.ReadBlock(buffer, index, count);    
    for (int i = 0; i < readCount; i++)
        AdvancePosition(buffer[index + i]);
    return readCount;
}
Run Code Online (Sandbox Code Playgroud)


Bin*_*ier 5

没有.

考虑到可以使用底层流对象(可以在任何行中的任何点)寻找任何poisition.现在考虑一下StreamReader保留的任何计数会做什么.

StreamReader应该去找出它现在在哪条线上吗?它是否应该只读取多行,而不管文件中的位置如何?

imho,还有更多的问题不仅仅是让这成为一个噩梦.

  • 获得代表+1的原因不应该取决于打字速度;-) (2认同)