0

I find it difficult to express myself so I'll jump right into the code

FileStream stream = new FileStream("//ignoreThis//demo.bin", 
                        FileMode.Append, FileAccess.Write, FileShare.Write));
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(myNum);
writer.Write(myString);

I can call this method several times and I know that every time I do so, it will add the new data at the end of the file 'demo.bin'. Now I'm trying to write a method where I can seek to my 3rd instance(I'm not sure if I can call it that). The main problem is that the variables are of a varied size, even though I'm using this code to make the String a 'fixed' length of 20 (I know it doesn't work when someone writes a string longer than 20 but I have already written validation to make sure they don't exceed this limit)

Here's the code I'm using to make the String a constant length

public String MakeFixedSize(int length, String inputString)
{
    return inputString.PadRight(length, '\0');
}

As far as I know, int is of a fixed size as well as long which I'm using in this program (not in above code)

TaW
  • 53,122
  • 8
  • 69
  • 111
Gabriel Stellini
  • 426
  • 4
  • 13
  • Have a look at [MSDN](https://msdn.microsoft.com/de-de/library/vstudio/yzxa6408%28v=vs.100%29.aspx?f=255&MSPPError=-2147217396) - It is in German, but you can switch the language, I hope. The code explains what to do when wrting(reading string with a binarywriter. The gist of it is that strings are written by binarywriter as 'length-prefixed' so you can teel the length by looking at the first two bytes. - But you may be better off using a `BinaryFormatter` and serializing the data. See [here](http://stackoverflow.com/questions/15750154/serialize-an-object-in-c-sharp-and-get-byte-stream) – TaW Oct 31 '15 at 19:06
  • What does 'write directly to the folder' mean? One file for each record? – TaW Oct 31 '15 at 19:49
  • I meant file. I want to separate each record in a distinguishable way so as to make searching faster, without using serialization (I.e. using a byte array and storing that via BinaryWriter and reading it back by parsing it back). I already have the code for the parsing and whatnot, but I need a good way to separate records – Gabriel Stellini Oct 31 '15 at 19:58
  • But with variable size records a direct seek won't work. The reading back in my example is done fast and directly, albeit on a record by record base. It is not (explicitly) parsing bytes, though, just the class member types. – TaW Oct 31 '15 at 20:02
  • So if I understood correctly, I need to prefix the string or serialization (which isn't allowed in this task). My question is why didn't the code I originally wrote to make strings a fixed size work with the binarywriter but worked fine when using serialisation? – Gabriel Stellini Oct 31 '15 at 20:16
  • Well, no, the prefix is created by the system, so when you want to use seek with fixed size string you should add those 2 bytes into the calculation as well as the length of your int. It should help to look at the result in a hex-editor.. Also you will need to know about your charcter format! Why it didn't work we can tell because you have not posted that code and not told us what errors it produced.. – TaW Oct 31 '15 at 20:19
  • After hours of pounding my head and pulling my fair apart, I finally found the bug. In my code I was doing something like this: class.name = MakeFixedSize(20, class.Name); Where class is a paramater – Gabriel Stellini Oct 31 '15 at 22:39
  • One last thing, what are you using to view the .bin? I'm using notepad++ but it doesn't show me any hex – Gabriel Stellini Oct 31 '15 at 22:42
  • There are many freeware hex editors; I used http://hexedit.nextsoft.de/ – TaW Oct 31 '15 at 22:44

1 Answers1

0

There are many ways to do this and I'm not sure which is the best for your case.

If you can and want to create strictly fixed size records you could go for seeking the right position directly. This may be faster for large quantities of data.

Or you could rely on the BinaryWriter to prefix each string with two length bytes and then read the right number of characters..

If you want to stay more flexible and keep things simple you could go for serializing with a BinaryFormatter:

Create a serializable class:

[Serializable]
public class MyClass
{
    public string Name;
    public int Number;
}

Then you can write a number of instances to disk using a BinaryFormatter:

string fileName = "yourfileName"; 

FileStream stream = new FileStream(fileName, 
                        FileMode.Append, FileAccess.Write, FileShare.Write);
var formatter = new BinaryFormatter();
formatter.Serialize(stream, new MyClass() { Name = "A", Number = 12365 });
formatter.Serialize(stream, new MyClass() { Name = "B", Number = 2365 });
formatter.Serialize(stream, new MyClass() { Name = "C", Number = 365 });
stream.Close();

And read it back, maybe in a List<T>, still using the same formatter:

FileStream stream2 = new FileStream(fileName, FileMode.Open, FileAccess.Read);
List<MyClass> myClassList = new List<MyClass>();

while (stream2.Position < stream2.Length)
    myClassList.Add((MyClass)formatter.Deserialize(stream2));

Now you can access myClassList[2]..

Update:

Looks like you can do this as well, as long as you are sure about the record length:

using (BinaryWriter binWriter = new BinaryWriter(File.Open(fileName, FileMode.Create)))
{
    binWriter.Write("RAller");
    binWriter.Write(123);
    binWriter.Write("RoBler");
    binWriter.Write(23);
    binWriter.Write("RolCer");
    binWriter.Write(3);
    binWriter.Write("RolDDr");
    binWriter.Write(423);
    binWriter.Write("REEler");
    binWriter.Write(523);
    binWriter.Write("RFFler");
    binWriter.Write(66);
}

using (BinaryReader br = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
    // string < 255 seems to get only a one byte length prefix:
    int recLen = 1 + 6 + 4;

    br.BaseStream.Seek(recLen * 3, SeekOrigin.Begin);
    string s = br.ReadString();
    int i = br.ReadInt32();
}

Short strings only get a one byte length counter:

enter image description here

Longer strings get the two byte prefix and for variable sizes you would have to step through the records, calculating the next offset.

I can't really image why you would want that, unless the data are really large.

Well, requirements sometimes are what they are and sometimes they require questioning..

TaW
  • 53,122
  • 8
  • 69
  • 111
  • The task specifically requests that I don't use serialization but rather store the values directly. Could you expand on prefixing a string? I'm unfamiliar with the concept – Gabriel Stellini Oct 31 '15 at 19:54
  • The [link](https://msdn.microsoft.com/de-de/library/yzxa6408%28v=vs.110%29.aspx) explains it rather well: _A length-prefixed string represents the string length by prefixing to the string a single byte or word that contains the length of that string. This method first writes the length of the string as a UTF-7 encoded unsigned integer, and then writes that many characters to the stream by using the BinaryWriter instance's current encoding._ It is worth studying the code, where you can see how to read back various types.. – TaW Oct 31 '15 at 20:00