已经针对 BaseStream 提出的观点是有效且重要的。但是,在某些情况下,您想阅读文本并知道您在文本中的位置。将其编写为一个类以使其易于重用仍然很有用。
我现在试着写这样一个类。它似乎工作正常,但速度相当慢。当性能不重要时应该没问题(它不是那么慢,见下文)。
无论您是一次读取一个字符、一次读取一个缓冲区还是一次读取一行,我都使用相同的逻辑来跟踪文本中的位置。虽然我确信通过放弃它可以使其执行得更好,但它更容易实现......并且我希望遵循代码。
我对StreamReader的ReadLine方法(我相信这是这个实现的最薄弱点)做了一个非常基础的性能比较,差异几乎是一个数量级。我使用我的 StreamReaderEx 类获得了 22 MB/s,但直接使用 StreamReader 的速度几乎是 9 倍(在我配备 SSD 的笔记本电脑上)。虽然这可能很有趣,但我不知道如何进行正确的阅读测试;也许使用 2 个相同的文件,每个文件都大于磁盘缓冲区,并交替读取它们..?至少我的简单测试在我多次运行时会产生一致的结果,无论哪个类首先读取测试文件。
NewLine 符号默认为 Environment.NewLine 但可以设置为长度为 1 或 2 的任何字符串。读者仅将此符号视为换行符,这可能是一个缺点。至少我知道 Visual Studio 已经多次提示我打开的文件“有不一致的换行符”。
请注意,我没有包括 Guard 类;这是一个简单的实用程序类,从上下文中应该知道如何替换它。您甚至可以删除它,但您会丢失一些参数检查,因此生成的代码将远离“正确”。例如,Guard.NotNull(s, "s") 只是检查 s 是否为空,如果是这种情况,则抛出 ArgumentNullException(参数名称为“s”,因此是第二个参数)。
废话不多说,代码如下:
公共类 StreamReaderEx : StreamReader
{
// 换行符(魔法值 -1:“未使用”)。
诠释新线1,新线2;
// 读取的最后一个字符是 NewLine 符号的第一个字符,并且我们使用的是两个字符的符号。
bool insideNewLine;
// StringBuilder 用于 ReadLine 实现。
StringBuilder lineBuilder = new StringBuilder();
public StreamReaderEx(string path, string newLine = "\r\n") : base(path)
{
初始化(新线);
}
public StreamReaderEx(Stream s, string newLine = "\r\n") : base(s)
{
初始化(新线);
}
公共字符串换行符
{
得到 { 返回 "" + (char)newLine1 + (char)newLine2; }
私人集
{
Guard.NotNull(value, "value");
Guard.Range(value.Length, 1, 2, "仅支持 1 到 2 个字符的换行符。");
newLine1 = 值[0];
newLine2 = (value.Length == 2 ? value[1] : -1);
}
}
公共 int LineNumber { 获取;私人套装;}
公共 int LinePosition { 获取;私人套装;}
公共覆盖 int Read()
{
int next = base.Read();
trackTextPosition(下一个);
下一个返回;
}
public override int Read(char[] buffer, int index, int count)
{
int n = base.Read(缓冲区,索引,计数);
对于 (int i = 0; 我