How to read binary files until EOF in C# - c#

I have a function to write some data into a binary file
private void writeToBinFile (List<MyClass> myObjList, string filePath)
{
FileStream fs = new FileStream(filePath, FileMode.Create);
BinaryWriter bw = new BinaryWriter(fs);
foreach (MyClass myObj in myObjList)
{
bw.Write(JsonConvert.SerializeObject(myObj));
}
bw.Close();
fs.Close();
}
I am looking something like
FileStream fs = new FileStream(filePath, FileMode.Create);
BinaryReader bw = new BinaryReader(fs);
while (!filePath.EOF)
{
List<MyClass> myObjList = br.Read(myFile);
}
anyone can help with this?
thanks in advance

JSON can be saved with no formatting (no new lines), so you can save 1 record per row of a file. Thus, my suggested solution is to ignore binary files and instead use a regular StreamWriter:
private void WriteToFile(List<MyClass> myObjList, string filePath)
{
using (StreamWriter sw = File.CreateText(filePath))
{
foreach (MyClass myObj in myObjList)
{
sw.Write(JsonConvert.SerializeObject(myObj, Newtonsoft.Json.Formatting.None));
}
}
}
private List<MyClass> ReadFromFile(string filePath)
{
List<MyClass> myObjList = new List<MyClass>();
using (StreamReader sr = File.OpenText(filePath))
{
string line = null;
while ((line = sr.ReadLine()) != null)
{
myObjList.Add(JsonConvert.DeserializeObject<MyClass>(line));
}
}
return myObjList;
}
If you really want to use the binary writer to save JSON, you could change it to be like this:
private void WriteToBinFile(List<MyClass> myObjList, string filePath)
{
using (FileStream fs = new FileStream(filePath, FileMode.Create))
using (BinaryWriter bw = new BinaryWriter(fs))
{
foreach (MyClass myObj in myObjList)
{
bw.Write(JsonConvert.SerializeObject(myObj));
}
}
}
private List<MyClass> ReadFromBinFile(string filePath)
{
List<MyClass> myObjList = new List<MyClass>();
using (FileStream fs = new FileStream(filePath, FileAccess.Read))
using (BinaryReader br = new BinaryReader(fs))
{
while (fs.Length != fs.Position) // This will throw an exception for non-seekable streams (stream.CanSeek == false), but filestreams are seekable so it's OK here
{
myObjList.Add(JsonConvert.DeserializeObject<MyClass>(br.ReadString()));
}
}
return myObjList;
}
Notes:
I've added using around your stream instantiations so that the files are properly closed when memory is freed
To check the stream is at the end, you have to compare Length to Position.

Related

Move position in FileStream (C#)

I have a txt file like this
#header1
#header2
#header3
....
#headerN
ID Value Pvalue
a 0.1 0.002
b 0.2 0.002
...
My code will try to parse
FileStream fs = new FileStream(file, FileMode.Open, FileMode.Read);
......
Table t = Table.Load(fs);
what I want is to make the start position of the Stream right before "ID", so I can feed the stream to the code and make a new table. But I am not sure what is the correct way to do it.
Thanks in advance
Ideally, you should convert Table.Load to take an IEnumerable<string> or at least a StreamReader, not a raw Stream.
If this is not an option, you can read the whole file into memory, skip its header, and write the result into MemoryStream:
MemoryStream stream = new MemoryStream();
using (var writer = new StreamWriter(stream, Encoding.UTF8);
foreach (var line in File.ReadLines(fileName).SkipWhile(s => s.StartsWith("#"))) {
writer.WriteLine(line);
}
}
stream.Position = 0;
Table t = Table.Load(stream);
Try this code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication57
{
class Program
{
const string file = "";
static void Main(string[] args)
{
FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);
StreamReader reader = new StreamReader(fs);
string inputline = "";
State state = State.FIND_HEADER;
while((inputline = reader.ReadLine()) != null)
{
switch (state)
{
case State.FIND_HEADER:
if (inputline.StartsWith("#header"))
{
state = State.READ_TABLE;
}
break;
case State.READ_TABLE:
Table t = Table.Load(fs);
break;
}
}
}
enum State
{
FIND_HEADER,
READ_TABLE
}
}
}

The process cannot access the file because it is being used by another process

public bool ReadFile()
{
string fname = "text.txt";
FileStream fs = null;
fs = new FileStream(fname, FileMode.OpenOrCreate,FileAccess.Read);
StreamReader sr = new StreamReader(fs);
string res = sr.ReadToEnd();
if (res == "1")
return true;
else
return false;
}
public void WriteToFile()
{
string fname = "text.txt";
FileStream fs = null;
fs = new FileStream(fname, FileMode.Open,FileAccess.Write);
StreamWriter sw = new StreamWriter(fs);
sw.Write("1");
}
So it should work like if ReadFile returns false than i do WriteFile.
But when it reaches writefile, it throws IO expection:
The process cannot access the file ... because it is being used by another process
You aren't closing the file when you read it.
Put your FileStream and StreamReader objects in using statements:
using (var fs = new FileStream(fname, FileMode.OpenOrCreate,FileAccess.Read)) {
using (var sr = new StreamReader(fs)) {
//read file here
}
}
Make sure you do the same when you write to the file.
You need to dispose the StreamReader object in the ReadFile method. The StreamReader inherits from IDisposable and therfor you need to dispose the object.
Check this link for more info:StreamReader Class

binary serialization to list

I used this information to convert a list to .txt with binary serialization. now I want to load that file, and put it again in my list.
this is my code to convert a list to .txt with binary serialization:
public void Save(string fileName)
{
FileStream fs = new FileStream(#"C:\" + fileName + ".txt", FileMode.Create);
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, list);
fs.Close();
}
so my question is; how to convert this binary file back to a list?
You can do it like this:
//Serialize: pass your object to this method to serialize it
public static void Serialize(object value, string path)
{
BinaryFormatter formatter = new BinaryFormatter();
using (Stream fStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
{
formatter.Serialize(fStream, value);
}
}
//Deserialize: Here is what you are looking for
public static object Deserialize(string path)
{
if (!System.IO.File.Exists(path)) { throw new NotImplementedException(); }
BinaryFormatter formatter = new BinaryFormatter();
using (Stream fStream = File.OpenRead(path))
{
return formatter.Deserialize(fStream);
}
}
Then use these methods:
string path = #"C:\" + fileName + ".txt";
Serialize(list, path);
var deserializedList = Deserialize(path);
Thanks #Hossein Narimani Rad , I used your answer and changed it a bit (so I understand it more) and now it works.
my binair serialize method (save) is still the same.
this is my binair deserialize method (load):
public void Load(string fileName)
{
FileStream fs2 = new FileStream(fileName, FileMode.Open);
BinaryFormatter binformat = new BinaryFormatter();
if (fs2.Length == 0)
{
MessageBox.Show("List is empty");
}
else
{
LoadedList = (List<Object>)binformat.Deserialize(fs2);
fs2.Close();
List.Clear();
MessageBox.Show(Convert.ToString(LoadedList));
List.AddRange(LoadedList);
}
I know I don't have an exception now, but I understand it better this way.
I also added some code to fill my listbox with my List with the new LoadedList.

GZip Decompression Gives Blank File

Given the code below, why is the decompression not working? "NewFile2.txt" should have the original, decompressed text, but the file is just blank.
ioTests.CompressFile(#"c:\newfile.txt", #"c:\newfile.txt.gz");
ioTests.DecompressFile(#"c:\newfile.txt.gz", #"c:\newfile2.txt");
public void CompressFile(string inFileName, string outFileName)
{
FileStream inFile = new FileStream(inFileName, FileMode.Open);
FileStream outFile = new FileStream(outFileName, FileMode.Create);
GZipStream compStream = new GZipStream(outFile, CompressionMode.Compress);
int theByte = inFile.ReadByte();
while (theByte != -1)
{
compStream.WriteByte((byte)theByte);
theByte = inFile.ReadByte();
}
compStream.Close();
}
public void DecompressFile(string inFileName, string outFileName)
{
FileStream inFile = new FileStream(inFileName, FileMode.Open);
FileStream outFile = new FileStream(outFileName, FileMode.CreateNew);
GZipStream compStream = new GZipStream(inFile, CompressionMode.Decompress);
int theByte = compStream.ReadByte();
while (theByte != -1)
{
outFile.WriteByte((byte)theByte);
theByte = compStream.ReadByte();
}
compStream.Close();
}
outFile.Flush(); // after your loop
I prefer
outFile.Close()
as that flushes the stream and calls the Dispose method, releasing allocated resources.
Since the streams you use implement the IDisposable interface, you should Dispose() / Close() your classes, or use the using statement to do this automatically:
using (FileStream inFile = new FileStream(inFileName, FileMode.Open))
using (FileStream outFile = new FileStream(outFileName, FileMode.Create))
using (GZipStream compStream = new GZipStream(outFile, CompressionMode.Compress)) {
int theByte = inFile.ReadByte();
// ... Rest of your code
}
This roughly translates to:
try {
FileStream inFile = new FileStream(inFileName, FileMode.Open);
FileStream outFile = new FileStream(outFileName, FileMode.Create);
GZipStream compStream = new GZipStream(outFile, CompressionMode.Compress);
int theByte = inFile.ReadByte();
// ... Rest of your code
} finally {
if (inFile != null) inFile.Dispose();
if (outFile != null) outFile.Dispose();
if (compStream != null) compStream.Dispose();
}

Reading file content changes in .NET

In Linux, a lot of IPC is done by appending to a file in 1 process and reading the new content from another process.
I want to do the above in Windows/.NET (Too messy to use normal IPC such as pipes). I'm appending to a file from a Python process, and I want to read the changes and ONLY the changes each time FileSystemWatcher reports an event. I do not want to read the entire file content into memory each time I'm looking for changes (the file will be huge)
Each append operation appends a row of data that starts with a unique incrementing counter (timestamp+key) and ends with a newline.
using (FileStream fs = new FileStream
(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (StreamReader sr = new StreamReader(fs))
{
while (someCondition)
{
while (!sr.EndOfStream)
ProcessLinr(sr.ReadLine());
while (sr.EndOfStream)
Thread.Sleep(100);
ProcessLinr(sr.ReadLine());
}
}
}
this will help you read only appended lines
You can store the offset of the last read operation and seek the file to that offset when you get a changed file notification. An example follows:
Main method:
public static void Main(string[] args)
{
File.WriteAllLines("test.txt", new string[] { });
new Thread(() => ReadFromFile()).Start();
WriteToFile();
}
Read from file method:
private static void ReadFromFile()
{
long offset = 0;
FileSystemWatcher fsw = new FileSystemWatcher
{
Path = Environment.CurrentDirectory,
Filter = "test.txt"
};
FileStream file = File.Open(
"test.txt",
FileMode.Open,
FileAccess.Read,
FileShare.Write);
StreamReader reader = new StreamReader(file);
while (true)
{
fsw.WaitForChanged(WatcherChangeTypes.Changed);
file.Seek(offset, SeekOrigin.Begin);
if (!reader.EndOfStream)
{
do
{
Console.WriteLine(reader.ReadLine());
} while (!reader.EndOfStream);
offset = file.Position;
}
}
}
Write to file method:
private static void WriteToFile()
{
for (int i = 0; i < 100; i++)
{
FileStream writeFile = File.Open(
"test.txt",
FileMode.Append,
FileAccess.Write,
FileShare.Read);
using (FileStream file = writeFile)
{
using (StreamWriter sw = new StreamWriter(file))
{
sw.WriteLine(i);
Thread.Sleep(100);
}
}
}
}

Categories

Resources