首页 文章

如何在异常情况下使文件流重新读取一行?

提问于
浏览
0

我有一个由streamreader读取的文件流 . 我想在IOException事件中将流的位置返回到前一行,以尝试再次读取同一行 . 我试图在行之前记录流的位置,然后寻求到那一点,但我必须误解或误用它 .

using (var fs = new FileStream("MyPath\\linetest.txt", FileMode.Open))
using (var sr = new StreamReader(fs))
{
    Console.WriteLine(fs.CanSeek);
    while (!sr.EndOfStream)
    {
        string line;
        try
        {
            var streamPosition = fs.Position;
            line = sr.ReadLine();
            if (line.StartsWith("#"))
            {
                fs.Seek(streamPosition, SeekOrigin.Begin);
                throw new IOException();
            }
            Console.WriteLine(line);
        }
        catch (IOException e)
        {
            Console.WriteLine(e.Message);
        }
    }

如果它不明显,我会在读取以“#”开头的行时抛出异常 . 我的7行测试文件中只有一行以#开头,所以我希望该行以无限循环写入,但是,当我执行此行时,它会打印文件的每一行,用异常消息替换该行 . 以 . . 开始 # . 任何洞察力或帮助你的家伙和女孩可以提供的将不胜感激!

1 回答

  • 0

    您的代码进入输入文件末尾的原因是 fs.Position 占据的位置是StreamReader在该阶段(位置A)从FileStream读取到 StreamReader's buffer 的位置 . 这与StreamReader通过 sr.ReadLine() (位置B)返回行的位置不同 . 当然,位置A总是领先于位置B.

    StreamReader没有提供位置B的方法 . 通过累计计算ReadLine()每次返回的内容来获取这样的物理位置并不是不可能的,但要考虑各种编码和EOL和BOM代码 . 那要准确起来并不容易 .

    相反,为什么在StreamReader解码后你没有获得逻辑位置?我的意思是,在这种情况下保留行号 .

    var row = 0;
    using (var fs = new FileStream("input/sample.txt", FileMode.Open)
    using (var sr = new StreamReader(fs))
    {
        Console.WriteLine(fs.CanSeek);
        while (!sr.EndOfStream)
        {
            try
            {
                var line = sr.ReadLine();
                if (line.StartsWith("#"))
                    throw new IOException();
                row++;
                Console.WriteLine(line);
            }
            catch (IOException e)
            {
                Console.WriteLine(e.Message);
    
                // Reset to the line where the exception happened
                fs.Seek(0, SeekOrigin.Begin);
                sr.DiscardBufferedData();
                for (var i = 0; i < row; i++)
                    sr.ReadLine();
            }
        }
    }
    

    catch块中的 sr.DiscardBufferedData() 用于清除我在开头提到的StreamReader中的缓冲区 . 请记住 sr.DiscardBufferedData() 会降低性能,这在here中提到 .

    我不明白你为什么要保持文件流打开并继续获得相同的错误结果 . 请注意,在流打开时,程序永远不会对文件进行更新 . 因此,我再留下一个代码,这些代码可以继续读取相同的行直到修正行,前提是已经处理过的行永远不会在换行符方面进行编辑 .

    var row = 0;
    while (true)
    {
        using (var fs = new FileStream("input/sample.txt", FileMode.Open))
        using (var sr = new StreamReader(fs))
        {
            for (var i = 0; i < row; i++)
                sr.ReadLine();
    
            try
            {
                while (!sr.EndOfStream)
                {
                    var line = sr.ReadLine();
                    if (line.StartsWith("#"))
                        throw new IOException();
                    row++;
                    Console.WriteLine(line);
                }
            }
            catch (IOException e)
            {
                Console.WriteLine(e.Message);
            }
            if (sr.EndOfStream)
                break;
        }
    }
    

    但是,无论哪种方式,即使您关注性能,从开头到错误行的重复读取也不是那么好 . 在这种情况下,您应该重新考虑设计输入数据的方式 .

相关问题