Write and Read an Array to a Binary File - c#

I have an array consisting of 1 string value and 2 int values, which I would like to write to a binary file.
It consists of name, index and score.
I have attached the array code below, how could I write this to a file?
Player[] playerArr = new Player[10];
int index = 0;
index = index + 1; // when a new player is added the index is increased by one
Player p = new Player(txtName3.Text, index, Convert.ToInt16(txtScore.Text)); // set the values of the object p
p.refName = txtName3.Text; // set refName to be the string value that is entered in txtName
p.refTotalScore = Convert.ToInt16(txtScore.Text);
playerArr[index] = p; // set the p object to be equal to a position inside the array
I would also like to sort each instantiation of the array to be output in descending order of score. How could this be done?
The file handling code I have so far is:
private static void WriteToFile(Player[] playerArr, int size)
{
Stream sw;
BinaryFormatter bf = new BinaryFormatter();
try
{
sw = File.Open("Players.bin", FileMode.Create);
bf.Serialize(sw, playerArr[0]);
sw.Close();
sw = File.Open("Players.bin", FileMode.Append);
for (int x = 1; x < size; x++)
{
bf.Serialize(sw, playerArr[x]);
}
sw.Close();
}
catch (IOException e)
{
MessageBox.Show("" + e.Message);
}
}
private int ReadFromFile(Player[] playerArr)
{
int size = 0;
Stream sr;
try
{
sr = File.OpenRead("Players.bin");
BinaryFormatter bf = new BinaryFormatter();
try
{
while (sr.Position < sr.Length)
{
playerArr[size] = (Player)bf.Deserialize(sr);
size++;
}
sr.Close();
}
catch (SerializationException e)
{
sr.Close();
return size;
}
return size;
}
catch (IOException e)
{
MessageBox.Show("\n\n\tFile not found" + e.Message);
}
finally
{
lstLeaderboard2.Items.Add("");
}
return size;
}

For the first part, you need to mark your class as Serializable, like this:
[Serializable]
public class Player
It's fine to Append to a new file, so you can change your code to this:
sw = File.Open(#"C:\Players.bin", FileMode.Append);
for (int x = 0; x < size; x++)
{
bf.Serialize(sw, playerArr[x]);
}
sw.Close();
(with the appropriate exception handling, and you'll obviously need to amend this if the file might already exist).
For the second part, you can sort an array like this using LINQ:
var sortedList = playerArr.OrderBy(p => p.Score);
If you require an array as output, do this:
var sortedArray = playerArr.OrderBy(p => p.Score).ToArray();
(Here, Score is the name of the property on the Player class by which you want to sort.)
If you'd like any more help, you'll need to be more specific about the problem!

Related

How to read 2D int array from binary file in c#?

I have a 2D integer array to store x,y coordinates. I checked out a few functions to write 2D array into a file but cannot find anything that is able to read that binary file on load and push it into a new 2 dimensional integer array.
This is my world generator function which saves it to the file:
public WorldGenerator()
{
int worldSizeX = 100;
int worldSizeY = 100;
int[,] world = new int[worldSizeX*worldSizeY, 2];
Logger.log("Generating world...");
for(int x = 0; x < worldSizeX; x++)
{
for(int y = 0; y < 2; y++)
{
System.Random random = new System.Random();
int itemID = random.Next(0, 1);
world[x, y] = itemID;
}
}
FileStream fs = new FileStream(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/ConsoleGame/world/default.wd", FileMode.OpenOrCreate, FileAccess.Write);
BinaryWriter bw = new BinaryWriter(fs);
for (int x = 0; x < worldSizeX; x++)
{
for (int y = 0; y < 2; y++)
{
bw.Write(world[x, y]);
}
}
bw.Close();
fs.Close();
Logger.log("World generated.");
}
Any good idea that could work for reading this file in? I should get back a 2D integer array and world[0,0] should get me the itemid. I am new to c# and this would be just a basic console application.
I have also seen others answering similar questions but none of them are worked for me yet. Maybe because this save function is wrong or something else.
EDIT:
Here is how I load the file:
using (var filestream = File.Open(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/ConsoleGame/world/default.wd", FileMode.Open))
using (var binaryStream = new BinaryReader(filestream))
{
while (binaryStream.PeekChar() != -1)
{
Console.WriteLine(binaryStream.ReadInt32());
}
}
need Newtonsoft.Json
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace ConsoleApp18
{
class Program
{
static void Main(string[] args)
{
int worldSizeX = 100;
int worldSizeY = 100;
int[,] world = new int[worldSizeX * worldSizeY, 2];
System.Random random = new System.Random();
for (int x = 0; x < worldSizeX; x++)
{
for (int y = 0; y < 2; y++)
{
int itemID = random.Next(0, 2);
world[x, y] = itemID;
}
}
string json = JsonConvert.SerializeObject(world, Formatting.Indented);
System.IO.File.WriteAllText("WriteText.txt", json);
string text = System.IO.File.ReadAllText("WriteText.txt");
int[,] deserialized = JsonConvert.DeserializeObject<int[,]>(text);
//use "deserialized"
}
}
}
What you need is called "Serialization".
Start with the simple builtin binary serializer.
Serializable attribute does the magic here.
By the moment you'll realize it is not the best option, you'll be able to use something more suiting your needs, like proto-buf.
I've also changed ints to shorts in your example. I doubt you need 32 bits for each world cell, so we can save a bit of hard drive space.
[Serializable]
public class WorldState
{
public short[,] Items { get; set; }
public void Save(string filename)
{
if (filename == null) throw new ArgumentNullException(nameof(filename));
using (var file = File.Create(filename))
{
var serializer = new BinaryFormatter();
serializer.Serialize(file, this);
}
}
public static WorldState Load(string filename)
{
if (filename == null) throw new ArgumentNullException(nameof(filename));
if (!File.Exists(filename)) throw new FileNotFoundException("File not found", filename);
using (var file = File.OpenRead(filename))
{
var serializer = new BinaryFormatter();
return serializer.Deserialize(file) as WorldState;
}
}
}
public class WorldStateTests
{
[Fact]
public void CanSaveAndLoad()
{
var ws = new WorldState
{
Items = new short[,]
{
{ 1, 2, 3, 4 },
{ 1, 2, 3, 4 },
{ 1, 2, 3, 4 },
{ 1, 2, 3, 4 }
}
};
// save the world state to file. Find it and see what's inside
ws.Save("./ws.bin");
// load the world back
var loaded = WorldState.Load("./ws.bin");
// check a new world state got loaded
Assert.NotNull(loaded);
// and it still has items
Assert.NotEmpty(loaded.Items);
// and the items are the same as we saved
Assert.Equal(ws.Items, loaded.Items);
}
}

Editing a line in a file by its number [duplicate]

This question already has answers here:
Edit a specific Line of a Text File in C#
(6 answers)
Closed 5 years ago.
I have to write an implementation of string that stores it's values on hard drive instead of ram (I know how stupid it sounds, but it's intended to teach us how different sorting algorithms work on ram and hard drive). This is what I've written so far:
class HDDArray : IEnumerable<int>
{
private string filePath;
public int this[int index]
{
get
{
using (var reader = new StreamReader(filePath))
{
string line = reader.ReadLine();
for (int i = 0; i < index; i++)
{
line = reader.ReadLine();
}
return Convert.ToInt32(line);
}
}
set
{
using (var fs = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
var reader = new StreamReader(fs);
var writer = new StreamWriter(fs);
for (int i = 0; i < index; i++)
{
reader.ReadLine();
}
writer.WriteLine(value);
writer.Dispose();
}
}
}
public int Length
{
get
{
int length = 0;
using (var reader = new StreamReader(filePath))
{
while (reader.ReadLine() != null)
{
length++;
}
}
return length;
}
}
public HDDArray(string file)
{
filePath = file;
if (File.Exists(file))
File.WriteAllText(file, String.Empty);
else
File.Create(file).Dispose();
}
public IEnumerator<int> GetEnumerator()
{
using (var reader = new StreamReader(filePath))
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return Convert.ToInt32(line);
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
The problem I'm facing is when trying to edit a line (in the the set portion of the indexer) I end up adding a new line instead of editing the old one (it's pretty obvious why, I just can't figure how to fix it).
Your array is designed to work with integers. Such a class is quite easy to create because the length of all numbers is 4 bytes.
class HDDArray : IEnumerable<int>, IDisposable
{
readonly FileStream stream;
readonly BinaryWriter writer;
readonly BinaryReader reader;
public HDDArray(string file)
{
stream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite);
writer = new BinaryWriter(stream);
reader = new BinaryReader(stream);
}
public int this[int index]
{
get
{
stream.Position = index * 4;
return reader.ReadInt32();
}
set
{
stream.Position = index * 4;
writer.Write(value);
}
}
public int Length
{
get
{
return (int)stream.Length / 4;
}
}
public IEnumerator<int> GetEnumerator()
{
stream.Position = 0;
while (reader.PeekChar() != -1)
yield return reader.ReadInt32();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Dispose()
{
reader?.Dispose();
writer?.Dispose();
stream?.Dispose();
}
}
Since the size of each array element is known, we can simply move to stream by changing its Position property.
BinaryWriter and BinaryReader are very comfortable to write and read numbers.
Open stream is a very heavy operation. Hence do it once when you create the class. At the end of the work, you need to clean up after themselves. So I implemented the IDisposable interface.
Usage:
HDDArray arr = new HDDArray("test.dat");
Console.WriteLine("Length: " + arr.Length);
for (int i = 0; i < 10; i++)
arr[i] = i;
Console.WriteLine("Length: " + arr.Length);
foreach (var n in arr)
Console.WriteLine(n);
// Console.WriteLine(arr[20]); // Exception!
arr.Dispose(); // release resources
I stand to be corrected, but I dont think there is an easy way to re-write a specific line, so you will probably find it easier to rewrite the file - modifying that line.
You could change your set code as follows:
set
{
var allLinesInFile = File.ReadAllLines(filepath);
allLinesInFile[index] = value;
File.WriteAllLines(filepath, allLinesInFile);
}
Goes without saying that there should be some safety checks in there to check the file exists and index < allLinesInFile.Length
I think for the sake of homework of sorting algorithms you needn't bother yourself memory size issues.
Of course please add checking file existing to read.
Note: Line counting in example starts from 0.
string[] lines = File.ReadAllLines(filePath);
using (StreamWriter writer = new StreamWriter(filePath))
{
for (int currentLineNmb = 0; currentLineNmb < lines.Length; currentLineNmb++ )
{
if (currentLineNmb == lineToEditNmb)
{
writer.WriteLine(lineToWrite);
continue;
}
writer.WriteLine(lines[currentLineNmb]);
}
}

c# How to run a application faster

I am creating a word list of possible uppercase letters to prove how insecure 8 digit passwords are this code will write aaaaaaaa to aaaaaaab to aaaaaaac etc. until zzzzzzzz using this code:
class Program
{
static string path;
static int file = 0;
static void Main(string[] args)
{
new_file();
var alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789+-*_!$£^=<>§°ÖÄÜöäü.;:,?{}[]";
var q = alphabet.Select(x => x.ToString());
int size = 3;
int counter = 0;
for (int i = 0; i < size - 1; i++)
{
q = q.SelectMany(x => alphabet, (x, y) => x + y);
}
foreach (var item in q)
{
if (counter >= 20000000)
{
new_file();
counter = 0;
}
if (File.Exists(path))
{
using (StreamWriter sw = File.AppendText(path))
{
sw.WriteLine(item);
Console.WriteLine(item);
/*if (!(Regex.IsMatch(item, #"(.)\1")))
{
sw.WriteLine(item);
counter++;
}
else
{
Console.WriteLine(item);
}*/
}
}
else
{
new_file();
}
}
}
static void new_file()
{
path = #"C:\" + "list" + file + ".txt";
if (!File.Exists(path))
{
using (StreamWriter sw = File.CreateText(path))
{
}
}
file++;
}
}
The Code is working fine but it takes Weeks to run it. Does anyone know a way to speed it up or do I have to wait? If anyone has a idea please tell me.
Performance:
size 3: 0.02s
size 4: 1.61s
size 5: 144.76s
Hints:
removed LINQ for combination generation
removed Console.WriteLine for each password
removed StreamWriter
large buffer (128k) for file writing
const string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789+-*_!$£^=<>§°ÖÄÜöäü.;:,?{}[]";
var byteAlphabet = alphabet.Select(ch => (byte)ch).ToArray();
var alphabetLength = alphabet.Length;
var newLine = new[] { (byte)'\r', (byte)'\n' };
const int size = 4;
var number = new byte[size];
var password = Enumerable.Range(0, size).Select(i => byteAlphabet[0]).Concat(newLine).ToArray();
var watcher = new System.Diagnostics.Stopwatch();
watcher.Start();
var isRunning = true;
for (var counter = 0; isRunning; counter++)
{
Console.Write("{0}: ", counter);
Console.Write(password.Select(b => (char)b).ToArray());
using (var file = System.IO.File.Create(string.Format(#"list.{0:D5}.txt", counter), 2 << 16))
{
for (var i = 0; i < 2000000; ++i)
{
file.Write(password, 0, password.Length);
var j = size - 1;
for (; j >= 0; j--)
{
if (number[j] < alphabetLength - 1)
{
password[j] = byteAlphabet[++number[j]];
break;
}
else
{
number[j] = 0;
password[j] = byteAlphabet[0];
}
}
if (j < 0)
{
isRunning = false;
break;
}
}
}
}
watcher.Stop();
Console.WriteLine(watcher.Elapsed);
}
Try the following modified code. In LINQPad it runs in < 1 second. With your original code I gave up after 40 seconds. It removes the overhead of opening and closing the file for every WriteLine operation. You'll need to test and ensure it gives the same results because I'm not willing to run your original code for 24 hours to ensure the output is the same.
class Program
{
static string path;
static int file = 0;
static void Main(string[] args)
{
new_file();
var alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789+-*_!$£^=<>§°ÖÄÜöäü.;:,?{}[]";
var q = alphabet.Select(x => x.ToString());
int size = 3;
int counter = 0;
for (int i = 0; i < size - 1; i++)
{
q = q.SelectMany(x => alphabet, (x, y) => x + y);
}
StreamWriter sw = File.AppendText(path);
try
{
foreach (var item in q)
{
if (counter >= 20000000)
{
sw.Dispose();
new_file();
counter = 0;
}
sw.WriteLine(item);
Console.WriteLine(item);
}
}
finally
{
if(sw != null)
{
sw.Dispose();
}
}
}
static void new_file()
{
path = #"C:\temp\list" + file + ".txt";
if (!File.Exists(path))
{
using (StreamWriter sw = File.CreateText(path))
{
}
}
file++;
}
}
your alphabet is missing 0
With that fixed there would be 89 chars in your set. Let's call it 100 for simplicity. The set you are looking for is all the 8 character length strings drawn from that set. There are 100^8 of these, i.e. 10,000,000,000,000,000.
The disk space they will take up depends on how you encode them, lets be generous - assume you use some 8 bit char set that contains the these characters, and you don't put in carriage returns, so one byte per char, so 10,000,000,000,000,000 bytes =~ 10 peta byes?
Do you have 10 petabytes of disk? (10000 TB)?
[EDIT] In response to 'this is not an answer':
The original motivation is to create the list? The shows how large the list would be. Its hard to see what could be DONE with the list if it was actualised, i.e. it would always be quicker to reproduce it than to load it. Surely whatever point could be made by producing the list can also be made by simply knowing it's size, which the above shows how to work it out.
There are LOTS of inefficiencies in you code, but if your questions is 'how can i quickly produce this list and write it to disk' the answer is 'you literally cannot'.
[/EDIT]

StreamReader using arraylist or array

Here's my code:
StreamReader reader = new StreamReader("war.txt");
string input = null;
while ((input = reader.ReadLine()) != null)
{
Console.WriteLine(input);
}
reader.Close();
The program above reads and prints out from the file “war.txt” line-by-line. I need to re-write the program so that it prints out in reverse order, i.e., last line first and first line last. For example, if “war.txt” contains the following:
Hello.
How are you?
Thank you.
Goodbye.
The program should prints out:
Goodbye.
Thank you.
How are you?
Hello.
I am very new in C# please help! Thanks!
To do that, you are going to have to buffer the data anyway (unless you do some tricky work with the FileStream API to read the file backwards). How about just:
var lines = File.ReadAllLines("war.txt");
for(int i = lines.Length - 1; i >= 0; i--)
Console.WriteLine(lines[i]);
which just loads the file (in lines) into an array, and then prints the array starting from the end.
A LINQ version of that would be:
foreach(var line in File.ReadLines("war.txt").Reverse())
Console.WriteLine(line);
but frankly the array version is more efficient.
You can do it using recursion with something like this:
void printReverse(int n)
{
String line = reader.readLine();
if (n > 0)
printReverse(n-1);
System.out.println(line);
}
Have a look at adding the lines to a List, then using Reverse on the list and then maybe the ForEach to output the items.
Another option: store each line into a Stack as you read them. After reading the file, pop the stack to print the lines in reverse order.
with the enumerable extension functions, this can be done shorter:
foreach(var l in File.ReadAllLines("war.txt").Reverse())
Console.WriteLine(l);
try
File.ReadAllLines(myFile)
.Reverse();
full code
var list = File.ReadAllLines(filepath).Reverse().ToList();
foreach (var l in list)
Console.WriteLine(l);
Implementation detail
Enumerable.Reverse Method - Inverts the order of the elements in a sequence
File.ReadAllLines Method (String) - Opens a text file, reads all lines of the file, and then closes the file.
here is a example mate, remember to add "using System.IO"
try
{
const int Size = 7;
decimal[] numbers = new decimal[Size];
decimal total = 0m;
int index = 0;
StreamReader inputfile;
inputfile = File.OpenText("Sales.txt");
while (index < numbers.Length && !inputfile.EndOfStream)
{
numbers[index] = decimal.Parse(inputfile.ReadLine());
index++;
}
inputfile.Close();
foreach (decimal Sales in numbers)
{
outputlistBox1.Items.Add(Sales);
total = total + Sales;
}
textBox1.Text = total.ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Here is another example from a textbook i bought few years ago, this has highest/lowest/average scores..(remember to use 'using System IO;)
private double Average(int[] iArray)
{
int total = 0;
double Average;
for (int index = 0; index < iArray.Length;
index++)
{
total += iArray[index];
}
Average = (double) total / iArray.Length;
return Average;
}
private int Highest(int[] iArray)
{
int highest = iArray[0];
for (int index = 1; index < iArray.Length; index++)
{
if (iArray[index] > highest)
{
highest = iArray[index];
}
}
return highest;
}
private int Lowest(int[] iArray)
{
int lowest = iArray[0];
for (int index = 1; index < iArray.Length; index++)
{
if (iArray[index] < lowest)
{
lowest = iArray[index];
}
}
return lowest;
}
private void button1_Click(object sender, System.EventArgs e)
{
try
{
const int SIZE = 5;
int[] Scores = new int [SIZE];
int index = 0;
int highestScore;
int lowestScore;
double averageScore;
StreamReader inputFile;
inputFile = File.OpenText("C:\\Users\\Asus\\Desktop\\TestScores.txt");
while (!inputFile.EndOfStream && index < Scores.Length)
{
Scores[index] = int.Parse(inputFile.ReadLine());
index++;
}
inputFile.Close();
foreach (int value in Scores)
{
listBox1.Items.Add(value);
}
highestScore = Highest(Scores);
lowestScore = Lowest(Scores);
averageScore = Average(Scores);
textBox1.Text = highestScore.ToString();
textBox2.Text = lowestScore.ToString();
textBox3.Text = averageScore.ToString("n1");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void button2_Click(object sender, EventArgs e)
{
this.Close();
}
}
}

Read double value from a file C#

I have a txt file that the format is:
0.32423 1.3453 3.23423
0.12332 3.1231 9.23432432
9.234324234 -1.23432 12.23432
...
Each line has three double value. There are more than 10000 lines in this file. I can use the ReadStream.ReadLine and use the String.Split, then convert it.
I want to know is there any faster method to do it.
Best Regards,
StreamReader.ReadLine, String.Split and Double.TryParse sounds like a good solution here.
No need for improvement.
There may be some little micro-optimisations you can perform, but the way you've suggested sounds about as simple as you'll get.
10000 lines shouldn't take very long - have you tried it and found you've actually got a performance problem? For example, here are two short programs - one creates a 10,000 line file and the other reads it:
CreateFile.cs:
using System;
using System.IO;
public class Test
{
static void Main()
{
Random rng = new Random();
using (TextWriter writer = File.CreateText("test.txt"))
{
for (int i = 0; i < 10000; i++)
{
writer.WriteLine("{0} {1} {2}", rng.NextDouble(),
rng.NextDouble(), rng.NextDouble());
}
}
}
}
ReadFile.cs:
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
public class Test
{
static void Main()
{
Stopwatch sw = Stopwatch.StartNew();
using (TextReader reader = File.OpenText("test.txt"))
{
string line;
while ((line = reader.ReadLine()) != null)
{
string[] bits = line.Split(' ');
foreach (string bit in bits)
{
double value;
if (!double.TryParse(bit, out value))
{
Console.WriteLine("Bad value");
}
}
}
}
sw.Stop();
Console.WriteLine("Total time: {0}ms",
sw.ElapsedMilliseconds);
}
}
On my netbook (which admittedly has an SSD in) it only takes 82ms to read the file. I would suggest that's probably not a problem :)
I would suggest reading all your lines at once with
string[] lines = System.IO.File.ReadAllLines(fileName);
This wold ensure that the I/O is done with the maximum efficiency. You woul have to measure (profile) but I would expect the conversions to take far less time.
your method is already good!
you can improve it by writing a readline function that returns an array of double and you reuse this function in other programs.
This solution is a little bit slower (see benchmarks at the end), but its nicer to read. It should also be more memory efficient because only the current character is buffered at the time (instead of the whole file or line).
Reading arrays is an additional feature in this reader which assumes that the size of the array always comes first as an int-value.
IParsable is another feature, that makes it easy to implement Parse methods for various types.
class StringSteamReader {
private StreamReader sr;
public StringSteamReader(StreamReader sr) {
this.sr = sr;
this.Separator = ' ';
}
private StringBuilder sb = new StringBuilder();
public string ReadWord() {
eol = false;
sb.Clear();
char c;
while (!sr.EndOfStream) {
c = (char)sr.Read();
if (c == Separator) break;
if (IsNewLine(c)) {
eol = true;
char nextch = (char)sr.Peek();
while (IsNewLine(nextch)) {
sr.Read(); // consume all newlines
nextch = (char)sr.Peek();
}
break;
}
sb.Append(c);
}
return sb.ToString();
}
private bool IsNewLine(char c) {
return c == '\r' || c == '\n';
}
public int ReadInt() {
return int.Parse(ReadWord());
}
public double ReadDouble() {
return double.Parse(ReadWord());
}
public bool EOF {
get { return sr.EndOfStream; }
}
public char Separator { get; set; }
bool eol;
public bool EOL {
get { return eol || sr.EndOfStream; }
}
public T ReadObject<T>() where T : IParsable, new() {
var obj = new T();
obj.Parse(this);
return obj;
}
public int[] ReadIntArray() {
int size = ReadInt();
var a = new int[size];
for (int i = 0; i < size; i++) {
a[i] = ReadInt();
}
return a;
}
public double[] ReadDoubleArray() {
int size = ReadInt();
var a = new double[size];
for (int i = 0; i < size; i++) {
a[i] = ReadDouble();
}
return a;
}
public T[] ReadObjectArray<T>() where T : IParsable, new() {
int size = ReadInt();
var a = new T[size];
for (int i = 0; i < size; i++) {
a[i] = ReadObject<T>();
}
return a;
}
internal void NextLine() {
eol = false;
}
}
interface IParsable {
void Parse(StringSteamReader r);
}
It can be used like this:
public void Parse(StringSteamReader r) {
double x = r.ReadDouble();
int y = r.ReadInt();
string z = r.ReadWord();
double[] arr = r.ReadDoubleArray();
MyParsableObject o = r.ReadObject<MyParsableObject>();
MyParsableObject [] oarr = r.ReadObjectArray<MyParsableObject>();
}
I did some benchmarking, comparing StringStreamReader with some other approaches, already proposed (StreamReader.ReadLine and File.ReadAllLines). Here are the methods I used for benchmarking:
private static void Test_StringStreamReader(string filename) {
var sw = new Stopwatch();
sw.Start();
using (var sr = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) {
var r = new StringSteamReader(sr);
r.Separator = ' ';
while (!r.EOF) {
var dbls = new List<double>();
while (!r.EOF) {
dbls.Add(r.ReadDouble());
}
}
}
sw.Stop();
Console.WriteLine("elapsed: {0}", sw.Elapsed);
}
private static void Test_ReadLine(string filename) {
var sw = new Stopwatch();
sw.Start();
using (var sr = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) {
var dbls = new List<double>();
while (!sr.EndOfStream) {
string line = sr.ReadLine();
string[] bits = line.Split(' ');
foreach(string bit in bits) {
dbls.Add(double.Parse(bit));
}
}
}
sw.Stop();
Console.WriteLine("elapsed: {0}", sw.Elapsed);
}
private static void Test_ReadAllLines(string filename) {
var sw = new Stopwatch();
sw.Start();
string[] lines = System.IO.File.ReadAllLines(filename);
var dbls = new List<double>();
foreach(var line in lines) {
string[] bits = line.Split(' ');
foreach (string bit in bits) {
dbls.Add(double.Parse(bit));
}
}
sw.Stop();
Console.WriteLine("Test_ReadAllLines: {0}", sw.Elapsed);
}
I used a file with 1.000.000 lines of double values (3 values each line). File is located on a SSD disk and each test was repeated multiple times in release-mode. These are the results (on average):
Test_StringStreamReader: 00:00:01.1980975
Test_ReadLine: 00:00:00.9117553
Test_ReadAllLines: 00:00:01.1362452
So, as mentioned StringStreamReader is a bit slower than the other approaches. For 10.000 lines, the performance is around (120ms / 95ms / 100ms).

Categories

Resources