When you need to reset a stream to beginning (e.g. MemoryStream) is it best practice to use
stream.Seek(0, SeekOrigin.Begin);
or
stream.Position = 0;
I've seen both work fine, but wondered if one was more correct than the other?
Use Position when setting an absolute position and Seek when setting a relative position. Both are provided for convenience so you can choose one that fits the style and readability of your code. Accessing Position requires the stream be seekable so they're safely interchangeable.
You can look at the source code for both methods to find out:
Position property
https://referencesource.microsoft.com/#mscorlib/system/io/memorystream.cs,320
Seek method
https://referencesource.microsoft.com/#mscorlib/system/io/memorystream.cs,482
The cost is almost identical (3 ifs and some arithmetics). However, this is only true for jumping to absolute offsets like Position = 0 and not relative offsets like Position += 0, in which case Seek seems slightly better.
However, you should keep in mind that we are talking about performance of a handful of integer arithmetics and if checks, that's like not even accurately measureable with benchmarking methods. Like others already pointed out, there is no significant/detectable difference.
If you are working with files (eg: with the FileStream class) it seems Seek(0, SeekOrigin.Begin) is able to keep internal buffer (when possible) while Position=0 will always discard it.
Related
I want to detect the encoding of a XML document before parsing it. So I found on stackoverflow this script.
public static XElement GetXMLFromStream(Stream uploadStream)
{
/** Remember position */
var position = uploadStream.Position;
/** Get encoding */
var xmlReader = new XmlTextReader(uploadStream);
xmlReader.MoveToContent();
/** Move to remembered position */
uploadStream.Seek(position, SeekOrigin.Begin); // with "pos" = 0 it not works, too
uploadStream.Seek(position, SeekOrigin.Current); // if I remove this I have the same issue!
/** Read content with detected encoding */
var streamReader = new StreamReader(uploadStream, xmlReader.Encoding);
var streamReaderString = streamReader.ReadToEnd();
return XElement.Parse(streamReaderString);
}
But it doesn't work.
Always I get EndOfStream true. But it isn't!!!! -.-
For example I have the string <test></test>.
Begin: 0, End: 13
If I ReadToEnd or MoveToContent then the end is reached successfully. The EndOfStream is true then.
If I reset the position via Seek or Position to 0 (for example) then a new StreamReader shows always EndOfStream is true.
The thing is that the uploadStream is a stream which I can not close.
It's a SharpZipLib stream of a http upload stream. So I can't close this stream. I can only working with it.
And the bad thing is only because Position and Seek not work... Only because ReadToEnd relays on this Position. - Else it would work. I think!
Maybe you can help my with this situation :-)
Thank you very much in Advance!
Example:
This approach is fundamentally incompatible with some types of input streams. Streams are not required to support Seek at all. In fact, Stream has a property specifically to detect whether Seek is usable, called CanSeek. Code needs to take into account that Seek can fail.
The simple but not very memory-efficient way is to copy your stream's content into a MemoryStream. That one does support Seek, and you can then do whatever you want with it. The fact that you're using ReadToEnd() suggests that the data is not so large that the memory use is going to cause a problem, so you can probably just go with this.
Note: as documented, if Seek is not supported, it's supposed to throw a NotSupportedException. It looks like with the stream implementation you're dealing with, it's not supported, but not properly implemented. I hope at least that CanSeek returns false for you, so you can still reliably detect this.
Option 1:
XElement has a Load() method that will read directly from an xml stream. It will manange the encoding for you internally. And it'll be more efficient by avoid a needless string. So why not use this.
XElement.Load(uploadStream);
Option 2:
If you really want to work with a string, dont use new XmlTextReader(). The XmlTextReader.Create() has more features so do this instead:
var xmlReader = XmlTextReader.Create(uploadStream);
var streamReaderString = xmlReader.ReadOuterXml();
return XElement.Parse(streamReaderString);
I have a MemoryStream /BinaryWriter , I use it as following:
memStram = new MemoryStream();
memStramWriter = new BinaryWriter(memStram);
memStramWriter(byteArrayData);
now to read I do the following:
byte[] data = new byte[this.BulkSize];
int readed = this.memStram.Read(data, 0, Math.Min(this.BulkSize,(int)memStram.Length));
My 2 question is:
After I read, the position move to currentPosition+readed , Does the
memStram.Length will changed?
I want to init the stream (like I just create it), can I do the following instead using Dispose and new again, if not is there any faster way than dispose&new: ;
memStram.Position = 0;
memStram.SetLength(0);
Thanks.
Joseph
No; why should Length (i.e. data size) change on read?
Yes; SetLength(0) is faster: there's no overhead with memory allocation and re-allocation in this case.
1: After I read, the position move to currentPosition+readed , Does the memStram.Length will changed?
Reading doesn't usually change the .Length - just the .Position; but strictly speaking, it is a bad idea even to look at the .Length and .Position when reading (and often: when writing), as that is not supported on all streams. Usually, you read until (one of, depending on the scenario):
until you have read an expected number of bytes, for example via some length-header that told you how much to expect
until you see a sentinel value (common in text protocols; not so common in binary protocols)
until the end of the stream (where Read returns a non-positive value)
I would also probably say: don't use BinaryWriter. There doesn't seem to be anything useful that it is adding over just using Stream.
2: I want to init the stream (like I just create it), can I do the following instead using Dispose and new again, if not is there any faster way than dispose&new:
Yes, SetLength(0) is fine for MemoryStream. It isn't necessarily fine in all cases (for example, it won't make much sense on a NetworkStream).
No the lenght should not change, and you can easily inspect that with a watch variable
i would use the using statement, so the syntax will be more elegant and clear, and you will not forget to dispose it later...
There's trouble using the System.IO.MemoryStream class.
After creating it, like so:
var memory = new MemoryStream();
it then sets the length of some bytes to write into it.
var length = 181;
memory.SetLength( length);
Then in the debugger, the memory shows the Length and
Position BOTH set to 181. In separate simply test program
it property shows Position still at zero after SetLength().
Furthermore, if I change the Position property to 0 using
the debugger or by adding a line of code, it ignores
and still shows 181 as the position property. Thus it
behaves as if immutable.
However, again in a simple unit test, this works as expected.
At first, this appeared to be a threading issue as
if MemoryStream isn't thread safe. But in the debugger,
I froze all other threads before calling any of this code.
And it still fails as above.
Well, this is the most bizarre. Any ideas what to try?
I don't see the same thing as you. If I create a console application with the following code in Main:
var x = new MemoryStream();
x.SetLength(181);
..and trace past the call to SetLength, the debugger shows Length equal to 181 and Position equal to 0. You must have something else affecting your stream object.
Actually, figured out the problem. First clue was that it only happened in the debugger.
The cause was the the ToString() method was overridden.
It was reading the memory and displaying it in the debugger. That was reading from the memory and therefore modifying the Position.
Problem solved.
Thanks.
Very strange behavior
If I create a bufferedstream on top of a file and then seek to an offset I get a block of bytes back
If I move the debugger back to the seek and re-seek I get an extra two characters
I ve triple checked this
Can there possibly be a bug with this class ?
If I reseek back to position I expect to get the same - The file has not changed - I open it in read only mode and I seek based on Origin
Reproduction:
bufferedStream.Seek(100,0, 100)
bufferedStream.Reade(buffer, 0, 100)
is different to what you get from here
bufferedStream.Seek(100,0, 100)
bufferedStream.Reade(buffer, 0, 100)
First off, it is hard to know if they are the same without checking the return value fro Read - is it possible they are just choosing different chunks? (perfectly valid; it is your job to ensure you loop over Read until you have enough data, or EOF).
However, I wonder if a BOM is involved here - especially if you are sitting a text-reader on top of this. Simply, a reader expects the BOM at the start; so it may well hide it the first time through. But if you rewind the stream while using the same reader/decoder, it won't be expecting a BOM, so will try to report it as character data (or throw an error, depending on the configuraion).
I have some difficulties with stream. I am using FileStream and BinaryReader and I got some weird behaviours. First of all (and this was on another question, when used StreamReader I got weird behaviour that when I did Peek the psoition was changed, so I used BinaryReader which was fine) NOW I have a problem that sometimes when I do Seek (using of course the underlying base stream - FileStream) SOMETIMES it works fine (get to the right position) but sometimes it just jumps to a position that is way beyond the file's length, It doesn't happen all the time, for instance I had a problem to get to a position at 1233*267, but a day later it was fine and the problem was at another place.
FileStream m_fsReader = new FileStream(m_strDataFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
BinaryReader m_brReader = new BinaryReader(m_fsReader);
and the seek part:
m_fsReader.Seek(offset, SeekOrigin.Begin);
Thanks,
I've noticed that every Stream keep its own position. When a Stream is constructed from another stream, the position is initially the same; but if the second stream seek, it doesn't synchronize its base stream position.
Try to watch Position property of both streams after read and seek operation. You will see discrepancies between the operation and the base stream Position value.
I solved this problem by calling myself Seek on the base stream after the work done by a substream.
It is difficult to say but I'm quite sure that is if one day work and another it does not probability the file has been changed.
Regarding the Seek Method it allow you to seek to any location beyond the length of the stream.
From MSDN:
You can seek to any location beyond the length of the stream. When you seek beyond the length of the file, the file size grows.
http://msdn.microsoft.com/en-us/library/system.io.filestream.seek.aspx