【问题标题】:Reading stream with 2 different readers具有 2 个不同阅读器的阅读流
【发布时间】:2013-03-01 22:49:07
【问题描述】:

我有一个文本文件,其中包含我要解析的固定长度表。但是,文件的开头是有关何时生成此表的一般信息(IE 时间、数据等)。

要阅读此内容,我尝试创建一个FileStream,然后使用StreamReader 阅读此文件的第一部分。我从文档的顶部解析出我需要的内容,然后当我完成后,将流的位置设置为结构化数据的第一行。

然后我将TextFieldParser 附加到流中(使用固定长度表的适当设置),然后尝试读取文件。在第一行,它失败了,在ErrorLine 属性中,它列出了表格第三行的后半部分。我浏览了它,它位于要阅读的第一行,但ErrorLine 属性却暗示了其他情况。

调试时,我发现如果在将TextFieldParser 附加到流后尝试使用StreamReader.ReadLine() 方法,前2 行显示正常。但是,当我阅读第三行时,它会返回一行,它从第三行的前半部分开始(并在ErrorLine 中的文本所在的位置停止)附加了文档后面的一些部分。如果我在附加 TextFieldParser 之前尝试此操作,它会读取所有 3 行。

我感觉这与我将 2 个读者绑定到同一个流有关。我不知道如何用结构化部分和非结构化部分来阅读这个,而不仅仅是自己标记这些行。我可以这样做,但我认为我不是第一个想以一种方式读取流的一部分,而以另一种方式读取流的后半部分的人。

为什么会这样跳过,你将如何读取不同格式的文本文件?

例子:

Date: 3/1/2013
Time: 3:00 PM
Sensor:  Awesome Thing

Seconds   X        Y          Value
0         5.1      2.8        55
30        4.9      2.5        33
60        5.0      5.3        44

为此简化示例量身定制的代码:

Boolean setupInfo = true;
DataTable result = new DataTable();
String[] fields;
Double[] dFields;

FileStream stream = File.Open(filePath,FileMode.Open);

StreamReader reader = new StreamReader(stream);

String tempLine;

for(int j = 1; j <= 7; j++)
{
   result.Columns.Add(("Column" + j));
}

//Parse the unstructured part
while(setupInfo)
{
   tempLine = reader.ReadLine();
   if( tempLine.StartsWith("Date:  "))
   {
       result.Rows.Add(tempLine);
   }
   else if (tempLine.StartsWith("Time:  "))
   {
       result.Rows.Add(tempLine);
   }
   else if (tempLine.StartsWith("Seconds")
   {
      //break out of this loop because the 
      //next line to be read is the unstructured part
      setupInfo =  false;
   }
}

//Parse the structured part
TextFieldParser parser = new TextFieldParser(stream);
parser.TextFieldType = FieldType.FixedWidth;
parser.HasFieldsEnclosedInQuotes = false;
parser.SetFieldWidths(10, 10, 10, 10);

while (!parser.EndOfData)
{
   if (reader.Peek() == '*')
   {
       break;
   }
   else
   {
       fields = parser.ReadFields();

       if (parseStrings(fields, out dFields))
       {
           result.Rows.Add(dFields);
       }
   }
}
return result;

【问题讨论】:

  • 你能发布你的代码吗?这将有助于识别问题
  • @VladL 好的,我添加了为此示例量身定制的代码。需要注意的一点是,我将数据添加到 DataTable 并从该函数返回。

标签: c# stream filestream streamreader fixed-width


【解决方案1】:

它跳过的原因是StreamReader 正在从FileStream 读取数据块,而不是逐个字符地读取。例如,StreamReader 可能从FileStream 中读取 4 KB,然后根据需要解析出这些行以响应 ReadLine() 调用。因此,当您将TextFieldParser 附加到FileStream 时,它将从当前文件位置读取——这是StreamReader 离开它的位置。

解决方案应该很简单:只需将TextFieldParser 连接到StreamReader

TextFieldParser parser = new TextFieldParser(reader);

TextFieldParser(TextReader reader)

【讨论】:

  • 这似乎可以解决问题。告诉我我是否理解正确。 Streamreader 在表格的第一行(文本方面)离开(文本方式),这(可以说)是块 3 的一部分。TextFieldParser.ReadFields(),然后开始读取块 4,它是下一个块。然后它失败了,因为它试图只解析我所说的宽度的一半。如果我传入StreamReader,它会强制它从下一个字符开始,而不是从下一个内存块开始?
  • @Xantham:是的,你已经掌握了这个概念。 StreamReader 在它的口袋里放了一些字符。通过将TextFieldReader 附加到StreamReader,您正在阅读这些字符。随着解析器继续读取,它从StreamReader 请求字符,然后从FileStream 获取数据并将其传递给解析器。
【解决方案2】:

一般来说,大多数流都在消耗 - 也就是说,一旦读取,它就不再可用。您可以通过编写一个从 Stream 派生的中间类来分叉到多个流,并且可以引发事件、重新发布到其他流等。

【讨论】:

    【解决方案3】:

    在您的情况下,您不需要StreamReader。最好的选择是使用File.ReadLines 方法检查文件内容。它不会加载整个文件内容,只会加载行,直到您找到所需的所有内容:

    foreach (string line in File.ReadLines(filePath))
    {
        if( line.StartsWith("Date:  "))
        {
            result.Rows.Add(line);
        }
        else if (line.StartsWith("Time:  "))
        {
            result.Rows.Add(line);
        }
        else if (line.StartsWith("Seconds"))
        {
           break;
        }
    }
    

    编辑

    您可以使用 LINQ 更简单:

    var d = from line in File.ReadLines(filePath) where line.Contains("Date:  ") select line;
    result.Rows.Add(d);
    

    【讨论】:

    • 但这如何帮助他解析文件的第二部分?
    • @JimMischel 据我所知,他在那里没有问题,只是使用两次流是一个问题
    • 我的意思是,除非我误解了,否则他会尝试将文件的前 N ​​行作为原始行读取,然后使用 TextFieldParser 读取文件的下一部分。他遇到的问题是如何在文件中的正确位置启动TextFieldParser
    猜你喜欢
    • 1970-01-01
    • 2013-03-13
    • 2016-02-14
    • 1970-01-01
    • 2012-04-11
    • 1970-01-01
    • 2014-07-11
    • 2014-10-31
    • 1970-01-01
    相关资源
    最近更新 更多