StreamReader和寻求

Sta*_*ker 12 c# streamreader

你可以使用streamreader来读取普通文本文件,然后在保存当前位置后再读取流读取器,然后再次打开streamreader并开始读取该poision吗?

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

这样的事情:

 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.
Run Code Online (Sandbox Code Playgroud)

@lasseespeholt

这是我试过的代码

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

Gra*_*ger 22

我意识到这真的很迟钝,但我偶然发现了这个令人难以置信的缺陷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;
}
Run Code Online (Sandbox Code Playgroud)

当然,这使用Reflection来获取私有变量,因此存在风险.但是,此方法适用于.Net 2.0,3.0,3.5,4.0,4.0.3,4.5,4.5.1,4.5.2,4.6和4.6.1.除了这个风险之外,唯一的另一个关键假设是底层字节缓冲区是a byte[1024]; 如果Microsoft以错误的方式更改它,则该方法会因UTF-16/-32而中断.

这已针对填充有Až?(10字节:)的UTF-8文件0x41 C5 BE E3 83 86 F0 A3 98 BA和填充有A(6字节:)的UTF-16文件进行了测试0x41 00 01 D8 37 DC.关键在于沿着byte[1024]边界强制分割字符,以及它们可能存在的所有不同方式.

更新(2013-07-03):我修复了该方法,该方法最初使用了其他答案中的破解代码.此版本已针对包含需要使用代理项对的字符的数据进行了测试.将数据放入3个文件中,每个文件具有不同的编码; 一个UTF-8,一个UTF-16LE和一个UTF-16BE.

更新(2016-02):处理二等分字符的唯一正确方法是直接解释基础字节.UTF-8处理得当,UTF-16/-32工作(给定byteBuffer的长度).


Las*_*olt 14

是的你可以,看到这个:

var sr = new StreamReader("test.txt");
sr.BaseStream.Seek(2, SeekOrigin.Begin); // Check sr.BaseStream.CanSeek first
Run Code Online (Sandbox Code Playgroud)

更新: 请注意,您不一定会使用sr.BaseStream.Position任何有用的东西,因为StreamReader它使用缓冲区,因此它不会反映您实际读取的内容.我想你会在找到真正的位置时遇到问题.因为你不能只统计字符(不同的编码,因此字符长度).我认为最好的方法是与FileStream自己合作.

更新: 使用TGREER.myStreamReader从这里:http: //www.daniweb.com/software-development/csharp/threads/35078 这个类添加BytesRead等(ReadLine()但显然不与其他读取方法一起工作)然后你可以这样做:

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

  • @marisks:daniweb 上提供的解决方案不处理多字节 UTF8 字符,因为它正在计算读取的*字符*数量以更新位置。 (2认同)