C# 流阅读器与搜索 你可以使用流读取器读取一个普通的文本文件,然后在阅读过程中关闭流读取器,保存当前的位置,然后再次打开流读取器,开始从那个测试中读取?

C# 流阅读器与搜索 你可以使用流读取器读取一个普通的文本文件,然后在阅读过程中关闭流读取器,保存当前的位置,然后再次打开流读取器,开始从那个测试中读取?,c#,streamreader,C#,Streamreader,如果不是,我还可以使用什么来完成相同的情况而不锁定文件 大概是这样的: var fs = File.Open(@"C:\testfile.txt", FileMode.Open, FileAccess.Read); var sr = new StreamReader(fs); Debug.WriteLine(sr.ReadLine());//Prints:firstline var pos = fs.Position; while

如果不是,我还可以使用什么来完成相同的情况而不锁定文件

大概是这样的:

 var fs = File.Open(@"C:\testfile.txt", FileMode.Open, FileAccess.Read);
        var sr = new StreamReader(fs);
        Debug.WriteLine(sr.ReadLine());//Prints:firstline
        var pos = fs.Position;
        while (!sr.EndOfStream)
        {
            Debug.WriteLine(sr.ReadLine());
        }
        fs.Seek(pos, SeekOrigin.Begin);
        Debug.WriteLine(sr.ReadLine());//Prints Nothing, i expect it to print SecondLine.
File.WriteAllText("test.txt", "1234\n56789");

long position = -1;

using (var sr = new myStreamReader("test.txt"))
{
    Console.WriteLine(sr.ReadLine());

    position = sr.BytesRead;
}

Console.WriteLine("Wait");

using (var sr = new myStreamReader("test.txt"))
{
    sr.BaseStream.Seek(position, SeekOrigin.Begin);
    Console.WriteLine(sr.ReadToEnd());
}

@拉塞斯波尔特

这是我试过的代码

            var position = -1;
        StreamReaderSE sr = new StreamReaderSE(@"c:\testfile.txt");
        Debug.WriteLine(sr.ReadLine());
        position = sr.BytesRead;
        Debug.WriteLine(sr.ReadLine());
        Debug.WriteLine(sr.ReadLine());
        Debug.WriteLine(sr.ReadLine());
        Debug.WriteLine(sr.ReadLine());
        Debug.WriteLine("Wait");
        sr.BaseStream.Seek(position, SeekOrigin.Begin);
        Debug.WriteLine(sr.ReadLine());
是的,您可以看到:

var sr = new StreamReader("test.txt");
sr.BaseStream.Seek(2, SeekOrigin.Begin); // Check sr.BaseStream.CanSeek first
更新: 请注意,您不一定要使用
sr.BaseStream.Position
对任何有用的内容进行定位,因为
StreamReader
使用缓冲区,因此它不会反映您实际读取的内容。我想你会很难找到真正的位置。因为你不能只计算字符(不同的编码,因此字符长度)。我认为最好的方法是使用
FileStream
本身

更新: 从这里使用
TGREER.myStreamReader
: 此类添加了
BytesRead
等(与
ReadLine()
一起使用,但显然不与其他读取方法一起使用) 然后你可以这样做:

 var fs = File.Open(@"C:\testfile.txt", FileMode.Open, FileAccess.Read);
        var sr = new StreamReader(fs);
        Debug.WriteLine(sr.ReadLine());//Prints:firstline
        var pos = fs.Position;
        while (!sr.EndOfStream)
        {
            Debug.WriteLine(sr.ReadLine());
        }
        fs.Seek(pos, SeekOrigin.Begin);
        Debug.WriteLine(sr.ReadLine());//Prints Nothing, i expect it to print SecondLine.
File.WriteAllText("test.txt", "1234\n56789");

long position = -1;

using (var sr = new myStreamReader("test.txt"))
{
    Console.WriteLine(sr.ReadLine());

    position = sr.BytesRead;
}

Console.WriteLine("Wait");

using (var sr = new myStreamReader("test.txt"))
{
    sr.BaseStream.Seek(position, SeekOrigin.Begin);
    Console.WriteLine(sr.ReadToEnd());
}
从MSDN:

StreamReader是为字符而设计的 输入特定的编码, 而流类是设计的 用于字节输入和输出。使用 StreamReader,用于读取 来自标准文本文件的信息


在大多数涉及
StreamReader
的示例中,您将看到使用ReadLine()逐行阅读。Seek方法来自
Stream
类,该类基本上用于读取或处理字节数据。

我意识到这真的太晚了,但我只是在
StreamReader
中偶然发现了这个难以置信的缺陷;使用
StreamReader
时无法可靠查找的事实。就我个人而言,我的特殊需要是具备阅读字符的能力,但如果满足某个条件,则需要“备份”;这是我正在分析的一种文件格式的副作用

使用
ReadLine()。我必须支持可配置的记录/行分隔符序列和转义分隔符序列。另外,我不想实现自己的缓冲区,这样我就可以支持“备份”和转义序列;这应该是
StreamReader
的工作

此方法按需计算基本字节流中的实际位置。它适用于UTF8、UTF-16LE、UTF-16BE、UTF-32LE、UTF-32BE和任何单字节编码(例如代码页1252、437、28591等),无论是否存在前导码/BOM。此版本不适用于UTF-7、Shift JIS或其他可变字节编码

当我需要搜索底层流中的任意位置时,我直接设置
BaseStream.position
,然后调用
DiscardBufferedData()
,使
StreamReader
恢复同步,以便下一次
Read()
/
Peek()
调用

还有一个友好的提醒:不要随意设置
BaseStream.Position
。如果将字符对分,将使下一个
Read()
无效,对于UTF-16/-32,也将使此方法的结果无效

public static long GetActualPosition(StreamReader reader)
{
    System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetField;

    // The current buffer of decoded characters
    char[] charBuffer = (char[])reader.GetType().InvokeMember("charBuffer", flags, null, reader, null);

    // The index of the next char to be read from charBuffer
    int charPos = (int)reader.GetType().InvokeMember("charPos", flags, null, reader, null);

    // The number of decoded chars presently used in charBuffer
    int charLen = (int)reader.GetType().InvokeMember("charLen", flags, null, reader, null);

    // The current buffer of read bytes (byteBuffer.Length = 1024; this is critical).
    byte[] byteBuffer = (byte[])reader.GetType().InvokeMember("byteBuffer", flags, null, reader, null);

    // The number of bytes read while advancing reader.BaseStream.Position to (re)fill charBuffer
    int byteLen = (int)reader.GetType().InvokeMember("byteLen", flags, null, reader, null);

    // The number of bytes the remaining chars use in the original encoding.
    int numBytesLeft = reader.CurrentEncoding.GetByteCount(charBuffer, charPos, charLen - charPos);

    // For variable-byte encodings, deal with partial chars at the end of the buffer
    int numFragments = 0;
    if (byteLen > 0 && !reader.CurrentEncoding.IsSingleByte)
    {
        if (reader.CurrentEncoding.CodePage == 65001) // UTF-8
        {
            byte byteCountMask = 0;
            while ((byteBuffer[byteLen - numFragments - 1] >> 6) == 2) // if the byte is "10xx xxxx", it's a continuation-byte
                byteCountMask |= (byte)(1 << ++numFragments); // count bytes & build the "complete char" mask
            if ((byteBuffer[byteLen - numFragments - 1] >> 6) == 3) // if the byte is "11xx xxxx", it starts a multi-byte char.
                byteCountMask |= (byte)(1 << ++numFragments); // count bytes & build the "complete char" mask
            // see if we found as many bytes as the leading-byte says to expect
            if (numFragments > 1 && ((byteBuffer[byteLen - numFragments] >> 7 - numFragments) == byteCountMask))
                numFragments = 0; // no partial-char in the byte-buffer to account for
        }
        else if (reader.CurrentEncoding.CodePage == 1200) // UTF-16LE
        {
            if (byteBuffer[byteLen - 1] >= 0xd8) // high-surrogate
                numFragments = 2; // account for the partial character
        }
        else if (reader.CurrentEncoding.CodePage == 1201) // UTF-16BE
        {
            if (byteBuffer[byteLen - 2] >= 0xd8) // high-surrogate
                numFragments = 2; // account for the partial character
        }
    }
    return reader.BaseStream.Position - numBytesLeft - numFragments;
}
publicstaticlongtactualposition(StreamReader)
{
System.Reflection.BindingFlags flags=System.Reflection.BindingFlags.DeclareOnly | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetField;
//解码字符的当前缓冲区
char[]charBuffer=(char[])reader.GetType().InvokeMember(“charBuffer”,标志,null,reader,null);
//要从charBuffer读取的下一个char的索引
int charPos=(int)reader.GetType().InvokeMember(“charPos”,标志,null,reader,null);
//当前在charBuffer中使用的解码字符数
int charLen=(int)reader.GetType().InvokeMember(“charLen”,标志,null,reader,null);
//读取字节的当前缓冲区(byteBuffer.Length=1024;这是关键)。
byte[]byteBuffer=(byte[])reader.GetType().InvokeMember(“byteBuffer”,标志,null,reader,null);
//将reader.BaseStream.Position提前到(重新)填充charBuffer时读取的字节数
int byteLen=(int)reader.GetType().InvokeMember(“byteLen”,标志,null,reader,null);
//剩余字符在原始编码中使用的字节数。
int numBytesLeft=reader.CurrentEncoding.GetByteCount(charBuffer,charPos,charLen-charPos);
//对于可变字节编码,处理缓冲区末尾的部分字符
int numFragments=0;
if(byteLen>0&!reader.CurrentEncoding.IsSingleByte)
{
if(reader.CurrentEncoding.CodePage==65001)//UTF-8
{
字节字节计数掩码=0;
而((byteBuffer[byteLen-numFragments-1]>>6)==2)//如果字节是“10xx xxxx”,则它是一个连续字节
byteCountMask |=(byte)(1>6)==3)//如果字节是“11xx xxxx”,它将启动一个多字节字符。
字节计数掩码|=(字节)(1和((字节缓冲[byteLen-numFragments]>>7-numFragments)=字节计数掩码))
numFragments=0;//字节缓冲区中没有要说明的部分字符
}
else if(reader.CurrentEncoding.CodePage==1200)//UTF-16LE
{
if(byteBuffer[byteLen-1]>=0xd8)//高代理
numFragments=2;//说明部分字符
}
else if(reader.CurrentEncoding.CodePage==1201)//UTF-16BE
{
if(byteBuffer[byteLen-2]>=0xd8)//高代理
numFragments=2;//说明部分字符
}
}
返回reader.BaseStream.Position-numbytsleft-numFragments;
}
当然,这使用反射来获取私有变量,因此存在风险。但是,此方法适用于.Net 2.0、3.0、3.5、4.0、4.0.3、4.5、4.5.1、4.5.2、4.6和4