Regex - Capture every line based on condition - c#

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;
}
}
}

Related

How to collect array of matched value from string?

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;
}
}
}
}
}
}
}

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>>();
}
}

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();
}
}
}

Grouping arrays together and sorting Multiple arrays around a chosen array

I have a set of different arrays that need to be grouped and sorted. However, I am unsure how I would go about grouping together the arrays so that they can be sorted. I am trying to make it so the user can choose which array within a set of grouped arrays will be the base of the sort.
By this I mean if I grouped together all the arrays titled as WS1 along with Month and Year (meaning Year, Month, afArray, rainArray, sunArray, tMaxArray, tMinArray) I would wish that I could target a certain array such as tmaxArray and sort all of the arrays to correspond with that array.
Data in the arrays would look sort of like:
Month | February March April May ...
Year | 1997 1997 1997 1997
afArray | 2 0 4 1
rainArray | 132.00 102.00 112.00 134.00
I'd wish that all of these arrays could be sorted according to that of (for example rainArray) leading to the output of:
Month | March April February May ...
Year | 1997 1997 1997 1997
afArray | 0 4 2 1
rainArray | 102.00 112.00 132.00 134.00
Can anyone give me any advice of how I could go about doing this if it can be done? And is it possible to be able to sort those via an actual algorithm not via Visual Studios built in sorting application?
Just to give some form of extra explanation of how the system is currently built
this is the current code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace Climate_Sorting_Application
{
public class Program
{
public static string cont = "Y";
public static string station;
public static string[] monthArray = File.ReadAllLines("Month.txt");
public static string[] yearArrayPre = File.ReadAllLines("Year.txt");
public static int[] afArray = File.ReadAllLines("WS1_AF.txt").Select(int.Parse).ToArray();
public static string[] rainArray = File.ReadAllLines("WS1_Rain.txt");
public static string[] sunArray = File.ReadAllLines("WS1_Sun.txt");
public static string[] tmaxArray = File.ReadAllLines("WS1_TMax.txt");
public static string[] tminArray = File.ReadAllLines("WS1_TMin.txt");
public static string[] af2Array = File.ReadAllLines("WS2_Rain.txt");
public static string[] rain2Array = File.ReadAllLines("WS2_Rain.txt");
public static string[] sun2Array = File.ReadAllLines("WS2_Sun.txt");
public static string[] tmax2Array = File.ReadAllLines("WS2_TMax.txt");
public static string[] tmin2Array = File.ReadAllLines("WS2_TMin.txt");
public static string arrayToAnalyse;
public static int t;
static void Main(string[] args)
{
while(cont == "Y")
{
Console.WriteLine("Please State which Station you wish to view data for");
Console.WriteLine("Please type either 1 or 2 now then hit enter");
station = Console.ReadLine();
if(station == "1")
{
}
if (station == "2")
{
}
else
{
Console.WriteLine("Not a valid input. Restarting process");
Console.WriteLine("---------------------------------------");
}
}
Console.WriteLine("Thank you for using the Data Sorting System");
Console.WriteLine("Hit Enter to Close Program");
Console.ReadLine();
}
private static void sortProcess()
{
}
}
}
Grouped arrays are an anti-pattern. It's a sign you should create a class with a property for each of the arrays you use currently, and then have one array of the new class type. Do this, and sorting your data becomes a simple .OrderBy() expression.
I wrote some code for you below, but it's all typed directly into the answer window, and even if it weren't I don't have your test data. But this should give you an idea of what you're going for... a little more code to load the data, but notice how little code you need to manipulate it once we have it ready:
namespace Climate_Sorting_Application
{
public class ClimateRecord
{
public DateTime RecordDate {get;set;}
public int StationId {get;set;}
public double RainFall {get;set;}
public double Sun {get;set;}
public double TMax {get;set;}
public double TMin {get;set;}
public double AF {get;set;}
public override string ToString()
{
return String.Format("{0,-15:MMMM yyyy}{1,4}{2,8:F4}{3,8:F4}{4,8:F4}{5,8:F4}{6,8:F4}{7,8:F4}",
RecordDate, StationId, AF, RainFail, Sun, TMax, TMin);
}
}
public class Program
{
static IList<ClimateRecord> LoadData()
{
var result = new List<ClimateRecord>();
using (var monthRdr = new StreamReader("Month.txt"))
using (var yearRdr = new StreamReader("Year.txt"))
using (var afRdr1 = new StreamReader("WS1_AF.txt"))
using (var rainRdr1 = new StreamReader("WS1_Rain.txt"))
using (var sunRdr1 = new StreamReader("WS1_Sun.txt"))
using (var tmaxRdr1 = new StreamReader("WS1_TMax.txt"))
using (var tminRdr1 = new StreamReader("WS1_TMin.txt"))
using (var afRdr2 = new StreamReader("WS2_AF.txt"))
using (var rainRdr2 = new StreamReader("WS2_Rain.txt"))
using (var sunRdr2 = new StreamReader("WS2_Sun.txt"))
using (var tmaxRdr2 = new StreamReader("WS2_TMax.txt"))
using (var tminRdr2 = new StreamReader("WS2_TMin.txt"))
{
string year = yearRdr.ReadLine();
while (year != null)
{
var recordDate = DateTime.ParseExact(year + " " + monthRdr.ReadLine() + " 01", "yyyy MMMM dd", null)
var ws1 = new ClimateRecord() {
RecordDate = recordDate,
StationId = 1,
AF = double.Parse(afRdr1.ReadLine()),
Sun = double.Parse(sunRdr1.ReadLine()),
RainFall = double.Parse(rainRdr1.ReadLine()),
TMax = double.Parse(tmaxRdr1.ReadLine()),
TMin = double.Parse(tminRdr1.ReadLine())
};
var ws2 = new ClimateRecord() {
RecordDate = recordDate,
StationId = 2,
AF = double.Parse(afRdr2.ReadLine()),
Sun = double.Parse(sunRdr2.ReadLine()),
RainFall = double.Parse(rainRdr2.ReadLine()),
TMax = double.Parse(tmaxRdr2.ReadLine()),
TMin = double.Parse(tminRdr2.ReadLine())
};
result.Add(ws1);
result.Add(ws2);
year = yearRdr.ReadLine();
}
}
return result;
}
static void PrintData(IEnumerable<ClimateRecord> data)
{
Console.WriteLine("{0,15}{1,4}{2,8}{3,8}{4,8}{5,8}{6,8}{7,8}",
"Month/Year", "WS", "AF", "Rain", "Sun", "T-Max", "T-Min");
foreach (var record in data) Console.WriteLine(record);
}
static void Main(string[] args)
{
var climateData = LoadData();
Console.WriteLine("Printing all data: ");
PrintData(climateData);
Console.WriteLine("\n\nPrinting Station 1 data:");
PrintData(climateData.Where(r => r.StationId == 1));
Console.WriteLine("\n\nPrinting Station 2 data:");
PrintData(climateData.Where(r => r.StationId == 2));
Console.WriteLine("\n\nPrinting Station 1 data ordered by rainfall descending:");
PrintData(climateData.Where(r => r.StationId == 1).OrderBy(r => r.RainFall * -1));
}
}
}

Need to split a string into substrings but can't use split

I have a string that looks like this:
123.45.67.890-1292 connected to EDS via 10.98.765.432-4300.
I need to split it like so:
"123.45.67.890-1292 connected to EDS via 10.98.765.432-4300."
-----+------- --+- -+- -----+------- --+-
| | | | |
ClientIP | ServiceName HostIP |
| |
ClientSession HostSession
I'm converting the code from vbscript that has a lot of complex InStr methods. Was wondering if there was a way to do this using a regEx.
(\d{,3}\.\d{,3}\.\d{,3}\.\d{,3})-(\d+) connected to ([A-Z]+) via (\d{,3}\.\d{,3}\.\d{,3}\.\d{,3})-(\d+)\.
Why can't you use split? Using regular expression for single task is inappropriate:
([^\-]+)\-(\S+)\s+connected\s+to\s+(\S+)\s+via\s+([^\-]+)\-(\S+)\.
C# code implementation (regular expression):
static void Main(string[] args)
{
String input = "123.45.67.890-1292 connected to EDS via 10.98.765.432-4300.";
String pattern = #"([^\-]+)\-(\S+)\s+connected\s+to\s+(\S+)\s+via\s+([^\-]+)\-(\S+)\.";
Match match = Regex.Match(input, pattern);
if (match.Success)
{
foreach (var group in match.Groups)
{
Console.WriteLine(group);
}
}
Console.ReadKey();
}
C# code implementation (splitting):
public class DTO
{
public string ClientIP { get; set; }
public string ClientSession { get; set; }
public string ServiceName { get; set; }
public string HostIP { get; set; }
public string HostSession { get; set; }
}
static void Main(string[] args)
{
String input = "123.45.67.890-1292 connected to EDS via 10.98.765.432-4300.";
String[] splits = input.Split(new char[] { ' ' });
DTO obj = new DTO();
for (int i = 0; i < splits.Length; ++i)
{
switch (i)
{
// connected
case 1:
// to
case 2:
// via
case 4:
{
break;
}
// 123.45.67.890-1292
case 0:
{
obj.ClientIP = splits[i].Split(new char[] { '-' })[0];
obj.ClientSession = splits[i].Split(new char[] { '-' })[1];
break;
}
// EDS
case 3:
{
obj.ServiceName = splits[i];
break;
}
// 10.98.765.432-4300.
case 5:
{
obj.HostIP = splits[i].Split(new char[] { '-' })[0];
obj.HostSession = splits[i].Split(new char[] { '-' })[1];
break;
}
}
}
Console.ReadKey();
}
(?<ClientIP>\d+\.\d+\.\d+\.\d+)-(?<ClientSession>\d+) connected to (?<ServiceName>.*?) via (?<HostIP>\d+\.\d+\.\d+\.\d+)-(?<HostSession>\d+)\.
Here's a RegExp to match/capture that:
([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)-([0-9]+) connected to ([a-zA-Z]+) via ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)-([0-9]+)
implementation:
string pat = #"([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)-([0-9]+) connected to ([a-zA-Z]+) via ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)-([0-9]+)";
Regex r = new Regex(pat, RegexOptions.IgnoreCase);
Match match = r.Match("123.45.67.890-1292 connected to EDS via 10.98.765.432-4300.");
foreach (var str in match.Groups)
Console.WriteLine(str);
Console.ReadKey();
Since I don't see why you rule out String.Split() :
var parts = test.Split(new string[] {" connected to ", " via "},
StringSplitOptions.None);
gives you
123.45.67.890-1292
EDS
10.98.765.432-4300
breaking of the -#### session parts would take 1 extra step, also possible with Split().
Or maybe easier:
var parts = test.Split(' ', '-');
and use parts 0, 1, 4, 6, 7

Categories