Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a string to a stream

Tags:

.net-core

f#

I'm trying to convert a string into a bytestream so that I can read said stream. So what I'm doing is that I convert the string into a char array and the write each char/byte into a MemoryStream which I later wrap in a BinaryReader

The way I've tried to implement it is like this:

let readData (data:string) = 
    let ms = new MemoryStream()
    let bw = new BinaryWriter(ms)
    data.ToCharArray() |> Array.iter (fun x -> bw.Write((byte)x))
    let br = new BinaryReader(bw.BaseStream)
    readStream 0 br

I later try to read it like this

let rec readStream c (br:BinaryReader) =
    let bytes = br.ReadBytes 16
    printfn "Size read is: %i" bytes.Length

But my result is always 0 bytes read, and I've verified that the string is not 0 bytes obviously. And I've tried calling Flush on the MemoryStream.

Am I missing something fundamental or is the another more obvious way to do this?

like image 993
Daniel Figueroa Avatar asked Dec 10 '22 11:12

Daniel Figueroa


2 Answers

This is a terribly inefficient way of producing a MemoryStream. Specifically for this kind of application, it has a constructor that takes a byte array:

let bytes = System.Text.Encoding.UTF8.GetBytes data
let stream = MemoryStream( bytes )

But even that may be an overkill. You haven't described your ultimate goal, but from your code it kind of looks like you just want to convert a string to a byte array. If that is true, all you need is just Encoding.GetBytes:

let readData (data:string) = System.Text.Encoding.UTF8.GetBytes data

.

On casting

Also note that when you do (byte)x, you apparently expect this to be a "convert to byte" operation, as in C#, but it's not. The "casting" operation in F# is expressed differently (with operators :> and :?>), and it can only be used for sub/supertypes (i.e. inherited classes and interfaces). Doesn't apply to primitives. There is no such thing as casting primitive types in F#.

What you're actually doing is calling a function called byte. So the parentheses are not needed there, function arguments in F# are specified with a space, i.e. byte x.

This function does indeed convert a char to a byte, but it does so by just taking the first byte of the char (chars are two-byte things, did you know?) and throwing away the second byte. This works fine when your char is ASCII (second byte is zero anyway), but will lose data when it's not. Try converting to byte and then back to char:

let x = 'a'
let y = char (byte x)
> val y : char = 'a'  // This worked

let x = 'Г'
let y = char (byte x)
> val y : char = '\019'  // Oops
like image 162
Fyodor Soikin Avatar answered Dec 14 '22 07:12

Fyodor Soikin


After writing to the MemoryStream, it's position is at the end. You need to reset the position to the start to read it all. You could add this to the start of your readStream function

br.BaseStream.Position <- 0L
like image 21
TheQuickBrownFox Avatar answered Dec 14 '22 08:12

TheQuickBrownFox