TextReader和peek下一行

4

我使用TextReader从文本文件中读取数据。

TextReader reader = new StreamReader(stream);
string line;
while ((line = reader.ReadLine()) != null)
 {
   //.......
 }

有时我需要从读取器中窥视下一行(或几行)。
那我该怎么做呢?
4个回答

6

编辑:已更新以允许任意数量的预览:

public class PeekingStreamReader : StreamReader
{
    private Queue<string> _peeks;

    public PeekingStreamReader(Stream stream) : base(stream)
    {
        _peeks = new Queue<string>();   
    }

    public override string ReadLine()
    {
        if (_peeks.Count > 0)
        {
            var nextLine = _peeks.Dequeue();
            return nextLine;
        }
        return base.ReadLine();
    }

    public string PeekReadLine()
    {
        var nextLine = ReadLine();
        _peeks.Enqueue(nextLine);
        return nextLine;
    }
}

和我之前看到的一样。这也不会对我有所帮助。 - Alan Coromano
@AlanDert 我已经更新了我的示例,使用Queue<string>来允许任意数量的peek。 - armen.shimoon
对于继承自StreamReader类的情况我会给出+1分,该类是一个具体类,而不是抽象类TextReader(我刚刚检查过,因为我知道有些事情让我感到困扰)。 - SAJ14SAJ
@armen.shimoon,抱歉,但是查看并不意味着更改读取器的状态。您的方法看起来像是查看,但实际上它并没有查看,而是读取了它们并更改了读取器的状态。 - Alan Coromano
方便,但是EndOfStream存在问题,因为它需要检查EndOfStream属性和没有预读的行。不幸的是,您无法覆盖基本属性,但是您可以很容易地添加自己的函数。 - Jack Aidley

3

你需要自己完成这个任务,但并不难:

public class PeekTextReader {
    string lastLine;
    private readonly TextReader reader;
    public PeekTextReader(TextReader reader) {
        this.reader = reader;
    }
    public string Peek() {
        return lastLine ?? (lastLine = reader.ReadLine());
    }
    public string ReadLine() {
        string res;
        if (lastLine != null) {
            res = lastLine;
            lastLine = null;
        } else {
            res = reader.ReadLine();
        }
        return res;
    }
}

请注意,一次查看操作会将该行锁定,直到您执行完整的ReadLine操作。

@AlanDert 对于 ReadLine 的调用者来说,并不会像 Peek 一样知道:对他们而言,调用 Peek 并不像读取一样,因为下一次调用 ReadLine 将把选定的行返回给您。换句话说,被查看的行并没有被消耗 - Sergey Kalinichenko
边缘情况——如果客户端在已经读取了最后一行物理行时调用Peek会发生什么?这些细节是强大的实用类中的致命问题。 - SAJ14SAJ
2
@alanDert - 你批评回答者只向前查看了一行,但这正是你要求的。如果你正在寻找一个通用的n-lookahead缓冲区,那么你需要实现它。然而,在这些情况下,你可能最好使用n行内存来构建你的程序,而不是n行向前查看,理论上它们本质上是相同的,但由于EOF条件,后者在实践中更容易实现。我知道唯一一个通常使用1符号向前查看的地方是递归下降解析器。 - SAJ14SAJ
@dasblinkenlight 对不起,你是完全正确的 - 我刚刚查看了API文档并回来更新,这是微软API比平均水平更友好的一个案例。我的唯一辩护是我在过去的多年中编写了太多的平台和环境,这是.NET没有的一个常见陷阱 :-) - SAJ14SAJ
1
+1. 我不会继承实际的读取器(除非您愿意提供其他读取方法的正确实现,可能包括异步版本)。我唯一要添加的是 IDisposable 来关闭内部读取器。 - Alexei Levenkov
显示剩余5条评论

1

在文本阅读器中,没有可靠的方法可以向后移动阅读位置。您可以尝试查找底层流,但这可能不可能(如果流不支持查找),或者如果阅读器内部发生任何类型的缓存,则可能无法获得所需的结果。

最可靠的方法是记住最后一行,您可以考虑创建自定义类,将其扩展为具有PeekString功能的阅读器...但是,如果您需要使用其他阅读器方法重新读取该字符串,则可能难以正确实现。


我更倾向于使用类似于while(not end of line) str+=reader.Peek()这样的东西。这有关系吗? - Alan Coromano

0

你可以使用“ReadAllLines”函数读取文件中的所有行,然后对它们进行简单的操作。但是请注意,如果文件过大,这将会占用过多的内存。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接