How to collect array of matched value from string? - c#

I am filling data from memory mapped file to string like :
AAPL,2013-1-2
Open:79.117
Close:78.433
High:79.286
Low:77.376
Volume:139948984
AAPL,2013-1-3
Open:78.268
Close:77.442
High:78.524
Low:77.286
Volume:88114464
and so on...
So now I want to make an array of close value of all days. And there are collection of thousands of days data in memory mapped file and string. So how can I fetch close value and can make array of its?
I am trying to make it's array but it's make whole data into single array. So it's not what i want.
string[] lines = System.IO.File.ReadAllLines(#"D:\mine.txt");
foreach (string line in lines)
{
// Use a tab to indent each line of the file.
Console.WriteLine("\t" + line);
}
byte[] bytes = new byte[10000000];
stream.ReadArray(0, bytes, 0, bytes.Length);
string txt = Encoding.UTF8.GetString(bytes).Trim('\0');`
So I need an array of all close value to fetch from that string. Like that:
{78.433, 77.442, etc..}

Try this:
decimal[] arrayOfCloses =
File
.ReadAllLines(#"D:\mine.txt")
.Select(x => x.Split(':'))
.Where(x => x.Length == 2)
.Where(x => x[0] == "Close")
.Select(x => decimal.Parse(x[1]))
.ToArray();

Try this:
File.ReadLines(#"D:\mine.txt")
// Pick only those lines starting with "Close"
.Where(line => line.StartsWith("Close:"))
// Get value, which follows colon, and parse it do double
.Select(line => double.Parse(line.Split(':')[1]))
// Convert result to an array
.ToArray();

I supposed your file Like this :
AAPL,2013-1-2
Open:79.117
Close:78.433
High:79.286
Low:77.376
Volume:139948984
AAPL,2013-1-3
Open:78.268
Close:77.442
High:78.524
Low:77.286
Volume:88114464
Try this
var lines = System.IO.File.ReadAllLines(#"C:\Users\bouyami\Documents\AB_ATELIER\1.txt").ToList();
var linesFiltred = lines.Where(x => x.StartsWith("Close")).ToList();
var result = linesFiltred.Select(x => x.Split(':')[1]).ToList();

Try following which accepts blank lines :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication98
{
class Program
{
const string FILENAME = #"c:\temp\test.txt";
static void Main(string[] args)
{
AAPL aapl = new AAPL(FILENAME);
}
}
public class AAPL
{
static List<AAPL> aapls = new List<AAPL>();
private DateTime date { get; set; }
public decimal open { get; set; }
public decimal close { get; set; }
public decimal low { get; set; }
public decimal high { get; set; }
public int volume { get; set; }
public AAPL() { }
public AAPL(string filename)
{
StreamReader reader = new StreamReader(filename);
string line = "";
AAPL newAAPL = null;
while ((line = reader.ReadLine()) != null)
{
line = line.Trim();
if (line.Length > 0)
{
if (line.StartsWith("AAPL"))
{
string dateStr = line.Substring(line.IndexOf(",") + 1);
date = DateTime.Parse(dateStr);
newAAPL = new AAPL();
aapls.Add(newAAPL);
newAAPL.date = date;
}
else
{
string[] splitArray = line.Split(new char[] { ':' });
switch (splitArray[0])
{
case "Open":
newAAPL.open = decimal.Parse(splitArray[1]);
break;
case "Close":
newAAPL.close = decimal.Parse(splitArray[1]);
break;
case "Low":
newAAPL.low = decimal.Parse(splitArray[1]);
break;
case "High":
newAAPL.high = decimal.Parse(splitArray[1]);
break;
case "Volume":
newAAPL.volume = int.Parse(splitArray[1]);
break;
}
}
}
}
}
}
}

Related

C# Read file, split at ; write in array

so I have a file like this:
1;2;5
1;3;3
1;4;3
1;5;1
1;6;0
and I want every number as easy accassible as possibe
so I thought multi dimentional array
that's the idea so far:
System.IO.StreamReader file = new System.IO.StreamReader(#"C:\Users\Hisfantor\Desktop\transport.txt");
int count = 0;
string linee;
string line;
string[] extract;
while ((linee = file.ReadLine()) != null)
{
count++;
}
textBox1.Text = Convert.ToString(count);
double[,] destinations = new double[(int)count, 3];
for (int i = 0; i < count; i++)
{
line = file.ReadLine();
extract = line.Split(';');
destinations[i, 1] = Convert.ToDouble(extract[0]);
destinations[i, 2] = Convert.ToDouble(extract[1]);
destinations[i, 3] = Convert.ToDouble(extract[2]);
listBox1.Items.Add(destinations[i, 1]);
}
file.Close();
I tried different things, but never get anything in the listbox(just for testing)
Try this:
using System.IO.StreamReader file = new System.IO.StreamReader(#"C:\Users\Hisfantor\Desktop\transport.txt");
int count = 0;
string line;
string[] extract;
while ((line = file.ReadLine()) != null)
{
extract = line.Split(';');
var lineValue = new LineValue()
{
Col1 = Convert.ToDouble(extract[0]),
Col2 = Convert.ToDouble(extract[1]),
Col3 = Convert.ToDouble(extract[2]),
};
listBox1.Items.Add(lineValue);
count++;
}
textBox1.Text = Convert.ToString(count);
public class LineValue
{
public double Col1 { get; set; }
public double Col2 { get; set; }
public double Col3 { get; set; }
public override string ToString() => $"{Col1};{Col2};{Col3}";
}
I suggest jagged array, i.e. array of array double[][] instead of 2D one (double[,]). Then you can query the file with a help of Linq:
using System.Linq;
...
double[][] destinations = File
.ReadLines(#"C:\Users\Hisfantor\Desktop\transport.txt")
.Where(line => !string.IsNullOrWhiteSpace(line))
.Select(line => line
.Split(';')
.Select(item => double.Parse(item))
.ToArray())
.ToArray();
foreach (var array in destinations)
listBox1.Items.Add(array[1]);
This is how I would approach it.
class Item
{
public int Value1 { get; set; }
public int Value2 { get; set; }
public int Value3 { get; set; }
}
string line;
List<Item> items = new List<Item>();
using (StreamReader reader = new StreamReader(path))
{
while ((line = reader.ReadLine()) != null)
{
string[] tokens = line.Split(';');
if (tokens.Length == 3)
{
int value;
Item item = new Item();
item.Value1 = int.TryParse(tokens[0], out value) ? value : 0;
item.Value2 = int.TryParse(tokens[1], out value) ? value : 0;
item.Value3 = int.TryParse(tokens[2], out value) ? value : 0;
items.Add(item);
}
}
}
This code reads a line at a time and populates a list of Items as it reads them. It includes error checking to avoid unexpected exceptions due to invalid inputs.
Also, you should wrap StreamReader in a using block to ensure it cleans up in a timely manner, even if an exception exits your method prematurely.

Combine multiple lines into 1 string with stream reader

I have a decently sized file (95K lines) that i need to parse through. For the following sample data...
<FIPS>10440<STATE>AL<WFO>BMX
8 32.319 32.316 -86.484 -86.487 32.316 -86.484
32.316 -86.484
102 32.501 31.965 -85.919 -86.497 32.496 -86.248
32.448 -86.181 32.432 -86.189 32.433 -86.125 32.417 -86.116
32.406 -86.049 32.419 -86.023 32.337 -85.991 32.333 -85.969
32.276 -85.919 32.271 -85.986 32.250 -85.999 31.968 -85.995
31.965 -86.302 32.052 -86.307 32.051 -86.406 32.245 -86.410
32.276 -86.484 32.302 -86.491 32.332 -86.475 32.344 -86.497
32.364 -86.492 32.378 -86.463 32.405 -86.460 32.414 -86.396
32.427 -86.398 32.433 -86.350 32.412 -86.310 32.441 -86.325
32.487 -86.314 32.473 -86.288 32.488 -86.260 32.501 -86.263
32.496 -86.248
What I need to do is read from one FIPS to the next FIPS and combine the lines within each group into one giant line like the following...
<FIPS>10440<STATE>AL<WFO>BMX 8 32.319 32.316 -86.484 -86.487 32.316 -86.484 32.316 -86.484...
<FIPS>10440<STATE>AL<WFO>BMX 102 32.501 31.965 -85.919 -86.497 32.496 -86.248 32.448 -86.181...
I currently have the following code (about my 6th variation for the day). What am I missing?
using (var reader = new StreamReader(winterBoundsPath))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine().Trim();
if (!Char.IsLetter(line[0]))
{
if (line.Contains("<FIPS>"))
{
var lineReplace = line.Replace('<', ' ').Replace('>', ' ');
string[] rawData = lineReplace.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
temp = new WinterJsonModel
{
FIPS = rawData[1],
State = rawData[3],
Center = rawData[5],
polyCoords = new List<polyCoordsJsonData>()
};
}
else
{
string[] rawData2 = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (rawData2.Count() > 1)
{
allValues.Add(listPointValue);
listPointValue = new List<string>();
}
// Add values to line
foreach (string value in rawData2)
{
listPointValue.Add(value);
}
}
}
}
reader.Close();
}
Judging from the sample you've given, the line breaks are CRLF characters. This means you really only need to know two things.
1. If the line contains "FIPS" as a string literal enclosed as a tag
2. if you've reached the end of a line that has a carriage return.
I'm going to ignore the JSON bit for now, because it's not part of your question. I'm assuming this means you have the JSON well-handled and if we get these strings how you want them, you've got it from there.
var x = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine().Trim();
if (line.Contains("<FIPS>"))
{
x.Add(line.Replace(Environment.NewLine, " "));
}
else
{
var s = String.Concat(x.Last(), line.Replace(Environment.NewLine, string.Empty), " ");
x[x.Count - 1] = s;
}
}
Much of the point here is to separate the organization of the data away from actually putting it into your object. From here, you can iterate through the list in a foreach, creating new objects based on the results of string.Split() on each string in your List<string>.
I've been parsing text files for over 40 years. Code below is sample of what I've done
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace Oppgave3Lesson1
{
class Program
{
const string FILENAME = #"c:\temp\test.txt";
static void Main(string[] args)
{
WinterJsonModel data = new WinterJsonModel();
data.ParseFile(FILENAME);
}
}
public class WinterJsonModel
{
public static List<WinterJsonModel> samplData = new List<WinterJsonModel>();
public string fips { get; set; }
public string state { get; set; }
public string wfo { get; set; }
public List<Group> groups = new List<Group>();
public void ParseFile(string winterBoundsPath)
{
WinterJsonModel winterJsonModel = null;
Group group = null;
List<KeyValuePair<decimal, decimal>> values = null;
using (var reader = new StreamReader(winterBoundsPath))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine().Trim();
if (line.Length > 0)
{
if (line.StartsWith("<FIPS>"))
{
winterJsonModel = new WinterJsonModel();
WinterJsonModel.samplData.Add(winterJsonModel);
string[] rawData = line.Split(new char[] { '<', '>' }, StringSplitOptions.RemoveEmptyEntries);
winterJsonModel.fips = rawData[1];
winterJsonModel.state = rawData[3];
winterJsonModel.wfo = rawData[5];
group = null; // very inportant line
}
else
{
decimal[] rawData = line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries).Select(x => decimal.Parse(x)).ToArray();
//if odd number of numbers in a line
if (rawData.Count() % 2 == 1)
{
group = new Group();
winterJsonModel.groups.Add(group);
group.id = (int)rawData[0];
//remove group number from raw data
rawData = rawData.Skip(1).ToArray();
}
for (int i = 0; i < rawData.Count(); i += 2)
{
group.values.Add(new KeyValuePair<decimal, decimal>(rawData[i], rawData[i + 1]));
}
}
}
}
}
}
}
public class Group
{
public int id { get; set; }
public List<KeyValuePair<decimal, decimal>> values = new List<KeyValuePair<decimal, decimal>>();
}
}

Regex - Capture every line based on condition

To revisit a solution I had here over a year ago:
/* ----------------- jobnameA ----------------- */
insert_job: jobnameA job_type: CMD
date_conditions: 0
alarm_if_fail: 1
/* ----------------- jobnameB ----------------- */
insert_job: jobnameB job_type: CMD
date_conditions: 1
days_of_week: tu,we,th,fr,sa
condition: s(job1) & s(job2) & (v(variable1) = "Y" | s(job1)) & (v(variable2) = "Y"
alarm_if_fail: 1
job_load: 1
priority: 10
/* ----------------- jobnameC ----------------- */
...
I use the following regex to capture each job that has uses a variable v(x) in its condition parameter (only jobnameB here matches):
(?ms)(^[ \t]*/\*[\s-]*([\w-]*)[\s-]*\*/)((?:(?:(?!^[ \t]*/\*[\s-]*[\w-]*[\s-]*\*/).)*?condition\: ([^\n\r]*v\([^\n\r]*)[ \t]*\))+(?:(?!^[ \t]*/\*[\s-]*[\w-]*[\s-]*\*/).)*)
I now need each line caught as parameter and value groups while satisfying the same conditions.
This regex will get each line with parameter and value as separate capture groups, but this wont take into account the presence of variables v(x), so it grabs all jobs:
(?:^([\w_]*\:) ([^\n]+))
And, the following expression will get me as far as the first line (insert_job) of the satisfying jobs, but it ends there instead of grabbing all parameters.
(?:^[ \t]*/\*[\s-]*[\w-]*[\s-]*\*/)(?:(?!^[ \t]*/\*[\s-]*[\w-]*[\s-]*\*/).)*?(?:^([\w_]*\:) ([^\n]+))
Any further help will be appreciated.
I think this would be much easier if you broke it up into steps. I am using LINQ for this:
var jobsWithVx = Regex.Matches(src, #"(?ms)(^[ \t]*/\*[\s-]*([\w-]*)[\s-]*\*/)((?:(?:(?!^[ \t]*/\*[\s-]*[\w-]*[\s-]*\*/).)*?condition\: ([^\n\r]*v\([^\n\r]*)[ \t]*\))+(?:(?!^[ \t]*/\*[\s-]*[\w-]*[\s-]*\*/).)*)").Cast<Match>().Select(m => m.Value);
var jobParameters = jobsWithVx.Select(j => Regex.Matches(j, #"(?ms)^([\w_]+\:) (.+?)$")).Select(m => m.Cast<Match>().Select(am => am.Groups));
Then you can work with the job parameters:
foreach (var aJobsParms in jobParameters) {
foreach (var jobParm in aJobsParms) {
// work with job and parm
}
// alternatively, convert to a Dictionary
var jobDict = aJobsParms.ToDictionary(jpgc => jpgc[1].Value, jpgc => jpgc[2].Value));
// then work with the dictionary
}
Sample that runs in LINQPad:
var src = #"/* ----------------- jobnameA ----------------- */
insert_job: jobnameA job_type: CMD
date_conditions: 0
alarm_if_fail: 1
/* ----------------- jobnameB ----------------- */
insert_job: jobnameB job_type: CMD
date_conditions: 1
days_of_week: tu,we,th,fr,sa
condition: s(job1) & s(job2) & (v(variable1) = ""Y"" | s(job1)) & (v(variable2) = ""Y""
alarm_if_fail: 1
job_load: 1
priority: 10
/* ----------------- jobnameC ----------------- */
";
var jobsWithVx = Regex.Matches(src, #"(?ms)(^[ \t]*/\*[\s-]*([\w-]*)[\s-]*\*/)((?:(?:(?!^[ \t]*/\*[\s-]*[\w-]*[\s-]*\*/).)*?condition\: ([^\n\r]*v\([^\n\r]*)[ \t]*\))+(?:(?!^[ \t]*/\*[\s-]*[\w-]*[\s-]*\*/).)*)").Cast<Match>().Select(m => m.Value);
var jobParameters = jobsWithVx.Select(j => Regex.Matches(j, #"(?ms)^([\w_]+\:) (.+?)$")).Select(m => m.Cast<Match>().Select(am => am.Groups));
jobParameters.Dump();
I've been parsing text files for over 40 years. If I can't do it nobody can. I tried for awhile to use Regex to split your 'name: value' inputs but was unsuccessful. So I finally wrote my own method. Take a look what I did with the days of the week
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.txt";
static void Main(string[] args)
{
Job.Load(FILENAME);
}
}
public class Job
{
public static List<Job> jobs = new List<Job>();
public string name { get;set;}
public string job_type { get;set;}
public int date_conditions { get; set;}
public DayOfWeek[] days_of_week { get; set; }
public string condition { get; set; }
public int alarm_if_fail { get; set; }
public int job_load { get; set; }
public int priority { get; set;}
public static void Load(string filename)
{
Job newJob = null;
StreamReader reader = new StreamReader(filename);
string inputLine = "";
while ((inputLine = reader.ReadLine()) != null)
{
inputLine = inputLine.Trim();
if ((inputLine.Length > 0) && (!inputLine.StartsWith("/*")))
{
List<KeyValuePair<string, string>> groups = GetGroups(inputLine);
foreach (KeyValuePair<string, string> group in groups)
{
switch (group.Key)
{
case "insert_job" :
newJob = new Job();
Job.jobs.Add(newJob);
newJob.name = group.Value;
break;
case "job_type":
newJob.job_type = group.Value;
break;
case "date_conditions":
newJob.date_conditions = int.Parse(group.Value);
break;
case "days_of_week":
List<string> d_of_w = new List<string>() { "su", "mo", "tu", "we", "th", "fr", "sa" };
newJob.days_of_week = group.Value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => (DayOfWeek)d_of_w.IndexOf(x)).ToArray();
break;
case "condition":
newJob.condition = group.Value;
break;
case "alarm_if_fail":
newJob.alarm_if_fail = int.Parse(group.Value);
break;
case "job_load":
newJob.job_load = int.Parse(group.Value);
break;
case "priority":
newJob.priority = int.Parse(group.Value);
break;
}
}
}
}
reader.Close();
}
public static List<KeyValuePair<string, string>> GetGroups(string input)
{
List<KeyValuePair<string, string>> groups = new List<KeyValuePair<string, string>>();
string inputLine = input;
while(inputLine.Length > 0)
{
int lastColon = inputLine.LastIndexOf(":");
string value = inputLine.Substring(lastColon + 1).Trim();
int lastWordStart = inputLine.Substring(0, lastColon - 1).LastIndexOf(" ") + 1;
string name = inputLine.Substring(lastWordStart, lastColon - lastWordStart);
groups.Insert(0, new KeyValuePair<string,string>(name,value));
inputLine = inputLine.Substring(0, lastWordStart).Trim();
}
return groups;
}
}
}

Compare text files in C# and remove duplicate lines

1.txt:
Origination,destination,datetime,price
YYZ,YTC,2016-04-01 12:30,$550
YYZ,YTC,2016-04-01 12:30,$550
LKC,LKP,2016-04-01 12:30,$550
2.txt:
Origination|destination|datetime|price
YYZ|YTC|2016-04-01 12:30|$550
AMV|YRk|2016-06-01 12:30|$630
LKC|LKP|2016-12-01 12:30|$990
I have two text files with ',' and '|' as separators, and I want to create a console app in C# which reads these two files when I pass an origination and destination location from command prompt.
While searching, I want to ignore duplicate lines, and I want to display the results in order by price.
The output should be { origination } -> { destination } -> datetime -> price
Need help how to perform.
Here's a simple solution that works for your example files. It doesn't have any error checking for if the file is in a bad format.
using System;
using System.Collections.Generic;
class Program
{
class entry
{
public string origin;
public string destination;
public DateTime time;
public double price;
}
static void Main(string[] args)
{
List<entry> data = new List<entry>();
//parse the input files and add the data to a list
ParseFile(data, args[0], ',');
ParseFile(data, args[1], '|');
//sort the list (by price first)
data.Sort((a, b) =>
{
if (a.price != b.price)
return a.price > b.price ? 1 : -1;
else if (a.origin != b.origin)
return string.Compare(a.origin, b.origin);
else if (a.destination != b.destination)
return string.Compare(a.destination, b.destination);
else
return DateTime.Compare(a.time, b.time);
});
//remove duplicates (list must be sorted for this to work)
int i = 1;
while (i < data.Count)
{
if (data[i].origin == data[i - 1].origin
&& data[i].destination == data[i - 1].destination
&& data[i].time == data[i - 1].time
&& data[i].price == data[i - 1].price)
data.RemoveAt(i);
else
i++;
}
//print the results
for (i = 0; i < data.Count; i++)
Console.WriteLine("{0}->{1}->{2:yyyy-MM-dd HH:mm}->${3}",
data[i].origin, data[i].destination, data[i].time, data[i].price);
Console.ReadLine();
}
private static void ParseFile(List<entry> data, string filename, char separator)
{
using (System.IO.FileStream fs = System.IO.File.Open(filename, System.IO.FileMode.Open))
using (System.IO.StreamReader reader = new System.IO.StreamReader(fs))
while (!reader.EndOfStream)
{
string[] line = reader.ReadLine().Split(separator);
if (line.Length == 4)
{
entry newitem = new entry();
newitem.origin = line[0];
newitem.destination = line[1];
newitem.time = DateTime.Parse(line[2]);
newitem.price = double.Parse(line[3].Substring(line[3].IndexOf('$') + 1));
data.Add(newitem);
}
}
}
}
I'm not 100% clear on what the output of your program is supposed to be, so I'll leave that part of the implementation up to you. My strategy was to use a constructor method that takes a string (that you will read from a file) and a delimiter (since it varies) and use that to create objects which you can manipulate (e.g. add to hash sets, etc).
PriceObject.cs
using System;
using System.Globalization;
namespace ConsoleApplication1
{
class PriceObject
{
public string origination { get; set; }
public string destination { get; set; }
public DateTime time { get; set; }
public decimal price { get; set; }
public PriceObject(string inputLine, char delimiter)
{
string[] parsed = inputLine.Split(new char[] { delimiter }, 4);
origination = parsed[0];
destination = parsed[1];
time = DateTime.ParseExact(parsed[2], "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
price = Decimal.Parse(parsed[3], NumberStyles.Currency, new CultureInfo("en-US"));
}
public override bool Equals(object obj)
{
var item = obj as PriceObject;
return origination.Equals(item.origination) &&
destination.Equals(item.destination) &&
time.Equals(item.time) &&
price.Equals(item.price);
}
public override int GetHashCode()
{
unchecked
{
var result = 17;
result = (result * 23) + origination.GetHashCode();
result = (result * 23) + destination.GetHashCode();
result = (result * 23) + time.GetHashCode();
result = (result * 23) + price.GetHashCode();
return result;
}
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
HashSet<PriceObject> list1 = new HashSet<PriceObject>();
HashSet<PriceObject> list2 = new HashSet<PriceObject>();
using (StreamReader reader = File.OpenText(args[0]))
{
string line = reader.ReadLine(); // this will remove the header row
while (!reader.EndOfStream)
{
line = reader.ReadLine();
if (String.IsNullOrEmpty(line))
continue;
// add each line to our list
list1.Add(new PriceObject(line, ','));
}
}
using (StreamReader reader = File.OpenText(args[1]))
{
string line = reader.ReadLine(); // this will remove the header row
while (!reader.EndOfStream)
{
line = reader.ReadLine();
if (String.IsNullOrEmpty(line))
continue;
// add each line to our list
list2.Add(new PriceObject(line, '|'));
}
}
// merge the two hash sets, order by price
list1.UnionWith(list2);
List<PriceObject> output = list1.ToList();
output.OrderByDescending(x => x.price).ToList();
// display output here, e.g. define your own ToString method, etc
foreach (var item in output)
{
Console.WriteLine(item.ToString());
}
Console.ReadLine();
}
}
}

Reading respective lines of multiple text files to be combined

Firstly, I'm new to C# and I'm having a hard time figuring out how I'd go about doing this.
Basically, I have multiple text files all with their own types of data. My aim is to read the first line of each of these files and combine them into one string so that I can sort them later by their respective days.
For example, in the first line of each file there could be the values...
File 1: 16/02/15
File 2: Monday
File 3: 75.730
File 4: 0.470
File 5: 75.260
File 6: 68182943
So I'd like to combine them in a string like so "16/02/15 Monday 75.730 0.470 75.260 68182943"
I'd also want to do this for the second, third, fourth line etc. There are a total of 144 entries or lines.
Here is the code I have so far. I'm unsure if I'm on the right track.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace BankAlgorithms
{
class Algorithms
{
static void Main(string[] args)
{
//Saves each individual text file into their own string arrays.
string[] Day = File.ReadAllLines(#"C:\Users\computing\Desktop\algorithms\CMP1124M_Assigment_Files\Day.txt");
string[] Date = File.ReadAllLines(#"C:\Users\computing\Desktop\algorithms\CMP1124M_Assigment_Files\Date.txt");
string[] Close = File.ReadAllLines(#"C:\Users\computing\Desktop\algorithms\CMP1124M_Assigment_Files\SH1_Close.txt");
string[] Diff = File.ReadAllLines(#"C:\Users\computing\Desktop\algorithms\CMP1124M_Assigment_Files\SH1_Diff.txt");
string[] Open = File.ReadAllLines(#"C:\Users\computing\Desktop\algorithms\CMP1124M_Assigment_Files\SH1_Open.txt");
string[] Volume = File.ReadAllLines(#"C:\Users\computing\Desktop\algorithms\CMP1124M_Assigment_Files\SH1_Volume.txt");
//Lists all files currently stored within the directory
string[] bankFiles = Directory.GetFiles(#"C:\Users\computing\Desktop\algorithms\CMP1124M_Assigment_Files");
Console.WriteLine("Bank Files currently saved within directory:\n");
foreach (string name in bankFiles)
{
Console.WriteLine(name);
}
Console.WriteLine("\nSelect the day you wish to view the data of (Monday-Friday). To view a grouped \nlist of all days, enter \"Day\"\n");
string selectedArray = Console.ReadLine();
if (selectedArray == "Day")
{
Console.WriteLine("Opening Day File...");
Console.WriteLine("\nDays grouped up in alphabetical order\n");
var sort = from s in Day
orderby s
select s;
foreach (string c in sort)
{
Console.WriteLine(c);
}
}
Console.ReadLine();
}
}
}
So this might be a little more than you strictly need, but I think it'll be robust, quite flexible and be able to handle huge files if need be.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ConsoleApplication2
{
class Program
{
private static void Main(string[] args)
{
// const string folder = #"C:\Users\computing\Desktop\algorithms\CMP1124M_Assigment_Files";
const string folder = #"C:\Temp\SO";
var filenames = new[] { #"Date.txt", #"Day.txt", #"SH1_Close.txt", #"SH1_Diff.txt", #"SH1_Open.txt", #"SH1_Volume.txt" };
var dataCombiner = new DataCombiner(folder, filenames);
var stockParser = new StockParser();
foreach (var stock in dataCombiner.GetCombinedData(stockParser.Parse)) //can also use where clause here
{
if (ShowRow(stock))
{
var outputText = stock.ToString();
Console.WriteLine(outputText);
}
}
Console.ReadLine();
}
private static bool ShowRow(Stock stock)
{
//use input from user etc...
return (stock.DayOfWeek == "Tuesday" || stock.DayOfWeek == "Monday")
&& stock.Volume > 1000
&& stock.Diff < 10; // etc
}
}
internal class DataCombiner
{
private readonly string _folder;
private readonly string[] _filenames;
public DataCombiner(string folder, string[] filenames)
{
_folder = folder;
_filenames = filenames;
}
private static IEnumerable<string> GetFilePaths(string folder, params string[] filenames)
{
return filenames.Select(filename => Path.Combine(folder, filename));
}
public IEnumerable<T> GetCombinedData<T>(Func<string[], T> parserMethod) where T : class
{
var filePaths = GetFilePaths(_folder, _filenames).ToArray();
var files = filePaths.Select(filePath => new StreamReader(filePath)).ToList();
var lineCounterFile = new StreamReader(filePaths.First());
while (lineCounterFile.ReadLine() != null)// This can be replaced with a simple for loop if the files will always have a fixed number of rows
{
var rawData = files.Select(file => file.ReadLine()).ToArray();
yield return parserMethod(rawData);
}
}
}
internal class Stock
{
public DateTime Date { get; set; }
public string DayOfWeek { get; set; }
public double Open { get; set; }
public double Close { get; set; }
public double Diff { get; set; }
public int Volume { get; set; }
public override string ToString()
{
//Whatever format you want
return string.Format("{0:d} {1} {2} {3} {4} {5}", Date, DayOfWeek, Close, Diff, Open, Volume);
}
}
internal class StockParser
{
public Stock Parse(string[] rawData)
{
//TODO: Error handling required here
var stock = new Stock();
stock.Date = DateTime.Parse(rawData[0]);
stock.DayOfWeek = rawData[1];
stock.Close = double.Parse(rawData[2]);
stock.Diff = double.Parse(rawData[3]);
stock.Open = double.Parse(rawData[4]);
stock.Volume = int.Parse(rawData[5]);
return stock;
}
public string ParseToRawText(string[] rawData)
{
return string.Join(" ", rawData);
}
}
}
PS:
Instead of reading it from the file, I'd rather also calculate the DayOfWeek from the Date.
Also be careful when parsing dates from a different locale (eg. USA vs UK).
If you have an option I'd only use the ISO 8601 datetime format.
Access your file strings from a collection, use this code to read from each file and use a StringBuilder to build your string.
Read only the first few lines of text from a file
var builder = new StringBuilder();
foreach(var file in fileList)
{
using (StreamReader reader = new StreamReader(file))
{
builder.Append(reader.ReadLine());
}
}
return builder.ToString();
You could use following approach: put all in an string[][] first, then it's easier:
string[][] all = { Day, Date, Close, Diff, Open, Volume };
To get the minimum length of all:
int commonRange = all.Min(arr => arr.Length);
Now this is all you need:
string[] merged = Enumerable.Range(0, commonRange)
.Select(i => string.Join(" ", all.Select(arr => arr[i])))
.ToArray();
This is similar to a for-loop from 0 to commonRange where you access all arrays with the same index and use String.Join to get a single string from all files' lines.
Since you have commented that you want to merge only the lines of a specific day:
var lineIndexes = Day.Take(commonRange)
.Select((line, index) => new { line, index })
.Where(x => x.line.TrimStart().StartsWith("Monday", StringComparison.InvariantCultureIgnoreCase))
.Select(x => x.index);
string[] merged = lineIndexes
.Select(i => string.Join(" ", all.Select(arr => arr[i])))
.ToArray();

Categories