2011-09-29 134 views
3

我写了这个小程序,它从Random.txt中读取每第5个字符 在random.txt中,我有一行文本:ABCDEFGHIJKLMNOPRST。我得到了预期的结果:为什么StreamReader.EndOfStream属性更改BaseStream.Position值

  • A的位置为0
  • 的F
  • 位置是K的5
  • 位置是P的10
  • 位置是15

这里是代码:

static void Main(string[] args) 
{ 
    StreamReader fp; 
    int n; 
    fp = new StreamReader("d:\\RANDOM.txt"); 
    long previousBSposition = fp.BaseStream.Position; 
    //In this point BaseStream.Position is 0, as expected 
    n = 0; 

    while (!fp.EndOfStream) 
    { 
     //After !fp.EndOfStream were executed, BaseStream.Position is changed to 19, 
     //so I have to reset it to a previous position :S 
     fp.BaseStream.Seek(previousBSposition, SeekOrigin.Begin); 
     Console.WriteLine("Position of " + Convert.ToChar(fp.Read()) + " is " + fp.BaseStream.Position); 
     n = n + 5; 
     fp.DiscardBufferedData(); 
     fp.BaseStream.Seek(n, SeekOrigin.Begin); 
     previousBSposition = fp.BaseStream.Position; 
    } 
} 

我的问题是,为什么行后BaseStream.Position变为19,例如结尾BaseStream。我的预期,显然是错误的,是BaseStream.Position将保持不变,当我打电话EndOfStream检查?

谢谢。

+3

StreamReader的内部有一个缓冲,让解码字节的文本。使用它的任何方法都会导致它从文件流中汲取字节。其头寸价值将不可预测。 –

+0

@HansPassant,我认为这是'在发布代码()调用'DiscardBufferedData的原因。 – svick

+0

@HansPassant,是的,我打我上面的代码,我注意到的StreamReader的Read()方法也导致在某些情况下BaseStream.Position的变化,所以它是不可预测的。 – vldmrrdjcc

回答

4

THRE唯一途径找出Stream是否处于其到底是要真正从中读到的东西,并检查返回值是否为0(StreamReader有另外一种方法–检查其内部缓冲区,但你正确地穿上”不要让它通过调用DiscardBufferedData来完成。)

因此,EndOfStream必须从基本流读取至少一个字节。由于逐字节读取效率低,因此读取更多。这就是为什么在调用EndOfStream改变位置到结束的原因(这woulnd't是文件的更大的文件末尾)。

看来你并不真正需要使用StreamReader,所以你应该使用Stream(或专门FileStream)直接:

using (Stream fp = new FileStream(@"d:\RANDOM.txt", FileMode.Open)) 
{ 
    int n = 0; 

    while (true) 
    { 
     int read = fp.ReadByte(); 
     if (read == -1) 
      break; 

     char c = (char)read; 
     Console.WriteLine("Position of {0} is {1}.", c, fp.Position); 
     n += 5; 
     fp.Position = n; 
    } 
} 

(我不知道是什么超出设定的结束位置文件就在这种情况下,你可能需要添加一个检查。)

+0

我想我明白了。还有一个问题:StreamReader不会缓冲整个流,而是缓冲整个流的一部分,所以在一些不可预知的时刻,当他需要更多数据时,它会从BaseStream中读取数据?我收到了吗? – vldmrrdjcc

+0

非常,是的。除了它不是不可预测的。当缓冲区为空且您需要读取一些字节时,可随时读取基本流。特别是在你发布的代码中,因为在丢弃之前你从缓冲区读取的内容不多。 – svick

+0

看来,EndOfStream正好读取了1024个下一个字节。如果在EndOfStream调用之后使用DiscardBufferedData,然后使用ReadLine,则将获得1024个字符后面的部分行。 –

1

你是对的,我也可以重现你的问题,无论如何根据(MSDN: Read Text from a File)用StreamReader读取文本文件的正确方法是以下,而不是你的(这也总是关闭和配置流通过使用使用块):

try 
{ 
    // Create an instance of StreamReader to read from a file. 
    // The using statement also closes the StreamReader. 
    using (StreamReader sr = new StreamReader("TestFile.txt")) 
    { 
     String line; 
     // Read and display lines from the file until the end of 
     // the file is reached. 
     while ((line = sr.ReadLine()) != null) 
     { 
      Console.WriteLine(line); 
     } 
    } 
} 
catch (Exception e) 
{ 
    // Let the user know what went wrong. 
    Console.WriteLine("The file could not be read:"); 
    Console.WriteLine(e.Message); 
} 
+0

如果你把一个破发点到线,而(!fp.EndOfStream),你会看到BaseStream.Position的是,行权后的变化,Console.WriteLine(前... – vldmrrdjcc

+0

你为什么不只是简单的阅读位置,而不是BaseStream.Position? –

+0

StreamReader没有自己的Position属性:S – vldmrrdjcc

2

的基本流的属性Position是指最后的读取字节的在缓冲器,而不是StreamReader的的光标的实际位置的位置。

+1

尽管文档没有清楚地说明这一点,但“位置”确实指的是字节,而不是缓冲区。我不确定那会是甚么意思。 – svick