【问题标题】:How do you cause a filestream to re-read a line in the event of an exception?如果发生异常,如何使文件流重新读取一行?
【发布时间】:2018-11-28 13:00:19
【问题描述】:

我有一个流读取器正在读取文件流。我想在发生 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 行测试文件中只有一行以 # 开头,所以我希望该行以无限循环的形式编写,但是,当我执行此操作时,它会打印文件的每一行,并将异常消息替换为以。。开始 #。我们将不胜感激您提供的任何见解或帮助!

【问题讨论】:

  • 为什么需要重读该行?是什么让你失去它?此外,它会打印每一行,因为您的 Console.WriteLine(line)if 语句主体之外。它会打印异常消息,因为您通过抛出异常来中断此流程,并且您不会以该行作为消息抛出异常。
  • “所以我希望那行写在一个无限循环中”你没有写那行。
  • 异常信息是否无限打印?
  • 您正在重置 FileStream 位置,但 StreamReader 已经“读取”过了该位置。
  • 风格注释:您不应该使用这样的异常。异常适用于真正的异常情况,而不是普通的控制流。

标签: c# stream filestream ioexception


【解决方案1】:

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

StreamReader没有办法提供Position 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;
    }
}

但是,无论哪种方式,即使您关心性能,从头到尾反复阅读也不是那么好。在这种情况下,您应该重新考虑如何输入数据的设计。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 2012-05-01
    • 1970-01-01
    相关资源
    最近更新 更多