Capture Stream Output to String - c#

I'm calling a library method that writes to a stream. But I want to write to a string. Is this possible? (I do not control the source code of the method I'm calling and so changing that is not an option.)
Experimenting, I tried something like this:
iCalendarSerializer serializer = new iCalendarSerializer();
MemoryStream stream = new MemoryStream();
serializer.Serialize(new iCalendar(), stream, System.Text.Encoding.UTF8);
byte[] buff = new byte[stream.Length];
stream.Read(buff, 0, (int)stream.Length);
But I get an error on the last line that's something about not being able to access a closed stream. Apparently, the Serialize() method closes the stream when it's done.
Are there other options?

How about byte[] buff = stream.ToArray()?

ToArray is one of 2 correct way of getting the data out of memory stream (the other one is GetBuffer and Length). It looks like you just want byte array sized to data of the stream and ToArray does exactly that.
Note that it is by design safe to call these 3 methods on disposed stream, so you can safely wrap using(stream) around the code that write some data to the stream.
In you case stream look to be disposed by serialization code (.Serialize).
iCalendarSerializer serializer = new iCalendarSerializer();
MemoryStream stream = new MemoryStream();
using(stream)
{
serializer.Serialize(new iCalendar(), stream, System.Text.Encoding.UTF8);
}
byte[] buff = stream.ToArray();

In your example you need to change the position of the stream before read takes place:
stream.Position = 0;
stream.Read(buff, 0, (int)stream.Length);
In order to write stream to string you can use StreamReader.ReadToEnd() method:
var reader = new StreamReader(stream);
var text = reader.ReadToEnd();

Related

memorystream(byte[]) vs memorystream.write(byte[])

I needed to put a byte to a memory stream so initially, I used:
byte[] Input;
using (MemoryStream mem = new MemoryStream())
{
mem.Write(Input, 0, (int)Input.Length);
StreamReader stream = new StreamReader(mem);
...
}
I wanted to use the Streamreader to read lines from a text file.
It didn't work.
Then I used
using (MemoryStream mem = new MemoryStream(Input))
instead and removed
mem.Write(Input, 0, (int)Input.Length);
It worked. I don't know why. Why did it work?
In your first approach, you use mem.Write(Input, 0, (int)Input.Length);. Note that MemoryStream.Write sets the stream read/write position behind the written data. In your example case this is equivalent with a position signifying the end of the stream. Trying to read from the MemoryStream again will not return any data, as the MemoryStream read/write position is at the end of the stream.
In your second approach, you passed the Input byte array as argument to the MemoryStream constructor. Providing the byte array through the constructor not only will make MemoryStream use this byte array, but more importantly it keeps the initial stream position of zero. Thus, when trying to read from the MemoryStream initialized in this way, the data contained in the input byte array will be returned as expected.
How to fix the problem with the first approach?
You can make the first approach with MemoryStream.Write working by simply setting the MemoryStream position back to the intended/original value (in your example case it would be zero) after writing the data to the MemoryStream:
byte[] Input;
using (MemoryStream mem = new MemoryStream())
{
mem.Write(Input, 0, (int)Input.Length);
mem.Position = 0;
using (StreamReader stream = new StreamReader(mem))
{
...
}
}

How to copy a Stream from the begining irrespective its current position

I got a file stream which has content read from a disk.
Stream input = new FileStream("filename");
This stream is to be passed to a third party library which after reading the stream, keeps the Stream's position pointer at the end of the file (as ususal).
My requirement is not to load the file from the desk everytime, instead I want to maintain MemoryStream, which will be used everytime.
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[32768];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
}
I have tried the above code. It works for the first very time to copy the input stream to output stream, but subsequent calls to CopyStream will not work as the source's Position will be at the end of the stream after the first call.
Are there other alternatives which copy the content of the source stream to another stream irrespective of the source stream's current Position.
And this code needs to run in thread safe manner in a multi threaded environment.
You can use .NET 4.0 Stream.CopyTo to copy your steam to a MemoryStream. The MemoryStream has a Position property you can use to move its postition to the beginning.
var ms = new MemoryStream();
using (Stream file = File.OpenRead(#"filename"))
{
file.CopyTo(ms);
}
ms.Position = 0;
To make a thread safe solution, you can copy the content to a byte array, and make a new MemoryStream wrapping the byte array for each thread that need access:
byte[] fileBytes = ms.ToArray();
var ms2 = new MemoryStream(fileBytes);
You should check the input stream's CanSeek property. If that returns false, you can only read it once anyway. If CanSeek returns true, you can set the position to zero and copy away.
if (input.CanSeek)
{
input.Position = 0;
}
You may also want to store the old position and restore it after copying.
ETA: Passing the same instance of a Stream around is not the safest thing to do. E.g. you can't be sure the Stream wasn't disposed when you get it back. I'd suggest to copy the FileStream to a MemoryStream in the beginning, but only store the byte content of the latter by calling ToArray(). When you need to pass a Stream somewhere, just create a new one each time with new MemoryStream(byte[]).

How to get a stream of an in memory string?

I have a string of data, I want to have a stream of it without saving it to storage as a file and then getting an stream of it. then I will send this stream to SkyDrive. anyway.
await client.UploadAsync(folderId, filename, stream, OverwriteOption.Overwrite);
Is there anyway to get a stream of an in-memory string value, without saving it to storage first?
You can write it into a MemoryStream:
using(var stream = new MemoryStream())
{
var writer = new StreamWriter(stream);
writer.Write(yourString);
writer.Flush();
stream.Position = 0;
await client.UploadAsync(folderId, filename, stream, OverwriteOption.Overwrite);
}
Sure, use the MemoryStream class. You could load it like this for example:
var stream = new MemoryStream(Encoding.ASCII.GetBytes("Here is my string."));
or, you can just Write new data to it like this:
var myString = "Hello, World!";
stream.Write(Encoding.ASCII.GetBytes(myString), 0, myString.Length);
You could of course use a different encoding if you needed to, say UTF8 for example.

Can't create MemoryStream

Is there any reason why this code shouldn't produce a memory stream with the word Slappy in it?
private MemoryStream StringBuilderToMemoryStream(StringBuilder source)
{
MemoryStream memoryStream = new MemoryStream();
StreamWriter streamWriter = new StreamWriter(memoryStream);
streamWriter.Write("slappy");
return memoryStream;
}
Even if I say streamWriter.Write(source.toString()); it fails.
Funny thing is, that it works on one of the methods that calls this routine but not on any of the others.
And the order I call them in makes no difference either.
But regardless, even when I call the above, from the method that works, the output is still an empty MemoryStream.
Any thoughts?
You don't flush the stream writer so the word never gets written to the memory stream.
Add the following after the call to streamWriter.Write:
streamWriter.Flush();
Furthermore, if you want to read that word later from the memory stream, make sure to reset its position, because after the Write it is located after the word slappy:
memoryStream.Position = 0;
If you don't want to call streamWriter.Flush(); you can set the AutoFlush-Property of the StreamWriter, at the moment you create it.
MemoryStream memoryStream = new MemoryStream();
StreamWriter streamWriter = new StreamWriter(memoryStream)
{
AutoFlush = true
}

C#: how to read a line from a stream and then start reading it from beginning?

I need to read the first line from a stream to determine file's encoding, and then recreate the stream with that Encoding
The following code does not work correctly:
var r = response.GetResponseStream();
var sr = new StreamReader(r);
string firstLine = sr.ReadLine();
string encoding = GetEncodingFromFirstLine(firstLine);
string text = new StreamReader(r, Encoding.GetEncoding(encoding)).ReadToEnd();
The text variable doesn't contain the whole text. For some reason the first line and several lines after it are skipped.
I tried everything: closing the StreamReader, resetting it, calling a separate GetResponseStream... but nothing worked.
I can't get the response stream again as I'm getting this file from the internet, and redownloading it again would be bad performance wise.
Update
Here's what GetEncodingFromFirstLine() looks like:
public static string GetEncodingFromFirstLine(string line)
{
int encodingIndex = line.IndexOf("encoding=");
if (encodingIndex == -1)
{
return "utf-8";
}
return line.Substring(encodingIndex + "encoding=".Length).Replace("\"", "").Replace("'", "").Replace("?", "").Replace(">", "");
}
...
// true
Assert.AreEqual("windows-1251", GetEncodingFromFirstLine(#"<?xml version=""1.0"" encoding=""windows-1251""?>"));
** Update 2 **
I'm working with XML files, and the text variable is parsed as XML:
var feedItems = XElement.Parse(text);
Well you're asking it to detect the encoding... and that requires it to read data. That's reading it from the underlying stream, and you're then creating another StreamReader around the same stream.
I suggest you:
Get the response stream
Retrieve all the data into a byte array (or MemoryStream)
Detect the encoding (which should be performed on bytes, not text - currently you're already assuming UTF-8 by creating a StreamReader)
Create a MemoryStream around the byte array, and a StreamReader around that
It's not clear what your GetEncodingFromFirstLine method does... or what this file really is. More information may make it easier to help you.
EDIT: If this is to load some XML, don't reinvent the wheel. Just give the stream to one of the existing XML-parsing classes, which will perform the appropriate detection for you.
You need to change the current position in the stream to the beginning.
r.Position = 0;
string text = new StreamReader(r, Encoding.GetEncoding(encoding)).ReadToEnd();
I found the answer to my question here:
How can I read an Http response stream twice in C#?
Stream responseStream = CopyAndClose(resp.GetResponseStream());
// Do something with the stream
responseStream.Position = 0;
// Do something with the stream again
private static Stream CopyAndClose(Stream inputStream)
{
const int readSize = 256;
byte[] buffer = new byte[readSize];
MemoryStream ms = new MemoryStream();
int count = inputStream.Read(buffer, 0, readSize);
while (count > 0)
{
ms.Write(buffer, 0, count);
count = inputStream.Read(buffer, 0, readSize);
}
ms.Position = 0;
inputStream.Close();
return ms;
}

Categories