Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何知道文本文件中streamreader的位置(行号)?_C#_Streamreader - Fatal编程技术网

C# 如何知道文本文件中streamreader的位置(行号)?

C# 如何知道文本文件中streamreader的位置(行号)?,c#,streamreader,C#,Streamreader,举个例子(这可能不是真实的生活,但要说明我的观点): GetLinePosition这里是streamreader的一个虚拟扩展方法。 这可能吗 当然我可以自己数数,但这不是问题。不,不太可能。“行号”的概念是基于已经读取的实际数据,而不仅仅是位置。例如,如果要将读取器搜索到任意位置,它实际上不会读取该数据,因此无法确定行号 要做到这一点,唯一的办法就是自己跟踪它。不 考虑到可以使用底层流对象(可能在任何行的任何点)查找任何位置。 现在考虑对流读取器保持的计数会有什么影响。< /P> Strea

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

GetLinePosition
这里是streamreader的一个虚拟扩展方法。 这可能吗


当然我可以自己数数,但这不是问题。

不,不太可能。“行号”的概念是基于已经读取的实际数据,而不仅仅是位置。例如,如果要将读取器搜索到任意位置,它实际上不会读取该数据,因此无法确定行号

要做到这一点,唯一的办法就是自己跟踪它。

考虑到可以使用底层流对象(可能在任何行的任何点)查找任何位置。 现在考虑对流读取器保持的计数会有什么影响。< /P> StreamReader应该去弄清楚它现在在哪一行吗? 它是否应该只读取多行,而不考虑文件中的位置


除了这些问题,还有更多的问题要解决,imho。

这里有一个人用ReadLine()方法实现了一个StreamReader,该方法可以注册文件位置

我想应该从StreamReader继承,然后将额外的方法添加到特殊类以及一些属性(_lineLength+_bytesRead):

//读取一行。一行定义为一系列字符,后跟
//回车符('\r')、换行符('\n')或回车符
//紧接着是换行。结果字符串不存在
//包含终止回车和/或换行符。返回
//如果已到达输入流的结尾,则值为null。
//
/// 
公共重写字符串读取行()
{
_线宽=0;
//if(流==null)
//__错误。ReaderClosed();
if(charPos==charLen)
{
if(ReadBuffer()==0)返回null;
}
StringBuilder sb=null;
做
{
int i=charPos;
做
{
char ch=charBuffer[i];
int-EolChars=0;
如果(ch='\r'| | ch='\n')
{
EolChars=1;
字符串s;
如果(sb!=null)
{
sb.Append(charBuffer,charPos,i-charPos);
s=sb.ToString();
}
其他的
{
s=新字符串(charBuffer、charPos、i-charPos);
}
charPos=i+1;
if(ch='\r'&&(charPos0))
{
if(charBuffer[charPos]='\n')
{
charPos++;
EolChars=2;
}
}
_lineLength=s.长度+EolChars;
_字节读取=_字节读取+_行长度;
返回s;
}
i++;
}而(i0);
字符串ss=sb.ToString();
_lineLength=不锈钢长度;
_字节读取=_字节读取+_行长度;
返回ss;
}

认为代码中存在一个小错误,因为字符串的长度用于计算文件位置,而不是使用实际读取的字节(不支持UTF8和UTF16编码的文件)。

为任何文本阅读器提供行计数包装器非常容易:

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++;
        }
    }
}
缺点(为了简洁起见):

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

  • public override int ReadBlock(char[]缓冲区、int索引、int计数){
    var readCount=_inner.ReadBlock(缓冲区、索引、计数);
    对于(int i=0;i
    已经针对基流提出的观点是有效且重要的。但是,在某些情况下,您需要阅读文本并知道自己在文本中的位置。将其作为一个类编写以便于重用仍然是有用的

    我现在试着写这样一门课。它似乎工作正常,但速度相当慢。当性能不重要时(速度没有那么慢,请参见下文),它应该很好

    我使用相同的逻辑来跟踪文本中的位置,不管您是一次读取一个字符、一次读取一个缓冲区还是一次读取一行。虽然我相信这可以
     // Reads a line. A line is defined as a sequence of characters followed by
     // a carriage return ('\r'), a line feed ('\n'), or a carriage return
     // immediately followed by a line feed. The resulting string does not
     // contain the terminating carriage return and/or line feed. The returned
     // value is null if the end of the input stream has been reached.
     //
     /// <include file='doc\myStreamReader.uex' path='docs/doc[@for="myStreamReader.ReadLine"]/*' />
     public override String ReadLine()
     {
              _lineLength = 0;
              //if (stream == null)
              //       __Error.ReaderClosed();
              if (charPos == charLen)
              {
                       if (ReadBuffer() == 0) return null;
              }
              StringBuilder sb = null;
              do
              {
                       int i = charPos;
                       do
                       {
                               char ch = charBuffer[i];
                               int EolChars = 0;
                               if (ch == '\r' || ch == '\n')
                               {
                                        EolChars = 1;
                                        String s;
                                        if (sb != null)
                                        {
                                                 sb.Append(charBuffer, charPos, i - charPos);
                                                 s = sb.ToString();
                                        }
                                        else
                                        {
                                                 s = new String(charBuffer, charPos, i - charPos);
                                        }
                                        charPos = i + 1;
                                        if (ch == '\r' && (charPos < charLen || ReadBuffer() > 0))
                                        {
                                                 if (charBuffer[charPos] == '\n')
                                                 {
                                                          charPos++;
                                                          EolChars = 2;
                                                 }
                                        }
                                        _lineLength = s.Length + EolChars;
                                        _bytesRead = _bytesRead + _lineLength;
                                        return s;
                               }
                               i++;
                       } while (i < charLen);
                       i = charLen - charPos;
                       if (sb == null) sb = new StringBuilder(i + 80);
                       sb.Append(charBuffer, charPos, i);
              } while (ReadBuffer() > 0);
              string ss = sb.ToString();
              _lineLength = ss.Length;
              _bytesRead = _bytesRead + _lineLength;
              return ss;
     }
    
    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++;
            }
        }
    }
    
    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;
    }
    
    public class StreamReaderEx : StreamReader { // NewLine characters (magic value -1: "not used"). int newLine1, newLine2; // The last character read was the first character of the NewLine symbol AND we are using a two-character symbol. bool insideNewLine; // StringBuilder used for ReadLine implementation. StringBuilder lineBuilder = new StringBuilder(); public StreamReaderEx(string path, string newLine = "\r\n") : base(path) { init(newLine); } public StreamReaderEx(Stream s, string newLine = "\r\n") : base(s) { init(newLine); } public string NewLine { get { return "" + (char)newLine1 + (char)newLine2; } private set { Guard.NotNull(value, "value"); Guard.Range(value.Length, 1, 2, "Only 1 to 2 character NewLine symbols are supported."); newLine1 = value[0]; newLine2 = (value.Length == 2 ? value[1] : -1); } } public int LineNumber { get; private set; } public int LinePosition { get; private set; } public override int Read() { int next = base.Read(); trackTextPosition(next); return next; } public override int Read(char[] buffer, int index, int count) { int n = base.Read(buffer, index, count); for (int i = 0; i
    class CountingReader : StreamReader {
        private int _lineNumber = 0;
        public int LineNumber { get { return _lineNumber; } }
    
        public CountingReader(Stream stream) : base(stream) { }
    
        public override string ReadLine() {
            _lineNumber++;
            return base.ReadLine();
        }
    }
    
    CountingReader reader = new CountingReader(file.OpenRead())
    
    var index = streamReader.GetPosition();
    var line1 = streamReader.ReadLine();
    
    streamReader.SetPosition(index);
    var line2 = streamReader.ReadLine();
    
    Assert.AreEqual(line1, line2);
    
    public static class StreamReaderExtensions
    {
        readonly static FieldInfo charPosField = typeof(StreamReader).GetField("charPos", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        readonly static FieldInfo byteLenField = typeof(StreamReader).GetField("byteLen", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        readonly static FieldInfo charBufferField = typeof(StreamReader).GetField("charBuffer", BindingFlags.NonPublic | 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);
        }
    }