I'm trying to set value to different variables from messy text.
Main Dishes
name;group;price;TAX;number;Id;;Fullname;Description;
Bigger modifiers
Name;group;price;TAX;number;Id;/some/additional/stuff
Smaller modifiers
Name;;price;TAX;number;Id;/some/additional/stuff
Text
Omlet;Second Dishes;40,0000;;00027;326ef70c-8d29-4c63-94ce-0580f26f84ab;Omlet with chicken and mushroom sauce;;;;;
Onions;Vegetables;4,1000;21;00021;fe5bab77-72cf-474e-acbc-1562c2f6aa37;0/1/1/1/6;;;;
Tomatoes;Vegetables;4,2000;21;00022;180fa908-9428-444e-a1df-5b74a40def64;0/1/1/1/7;;;;
Day Soup;Soup;123,4560;9;10108;19674f89-a44a-423d-ae79-0fc020be8d72;;;;;;
Roast pork with sauce;Second Dishes;0,0500;21;1167;a929bf86-2b89-4af6-baf9-f37317e0d75f;;;;;;
Cucumbers;;0,5500;21;222;8e370b64-b1f8-4665-95ae-88327d877394;-/-/1/0/3;;;;;
Tomatoes with garlic;Vegetables;0,1100;21;00024;52d08882-41c2-4dc3-8c4b-998109b6aedc;-/-/1/0/3;;;;;
Salt;;0,3300;21;00025;39332fab-99e0-4663-a59a-fff0deab958d;-/-/1/0/3;;;;;
I have created a class
class Food
{
public string Name;
public string Group;
public string Price;
public string TaxPercent;
public string Number;
public string ID;
public string Type;
public string FullName;
public string Description;
}
Can someone explain me how i suppose to seperate part to smaller modifiers and bigger ones? Should I create new class or it is possible to work with one?
How should IF statement look like?
What i tried to so far
private void button1_Click(object sender, EventArgs e)
{
List<Food> List= new List<Food>();
using (StreamReader sr = new StreamReader(#"TEST.txt"))
{
while (!sr.EndOfStream)
{
string str;
string[] stringArray;
str = sr.ReadLine();
strArray = str.Split(';');
Food Dish = new Food();
Dish.Name = stringArray[0];
Dish.Group = stringArray[1];
Dish.Price = stringArray[2];
Dish.TaxPercent = stringArray[3];
List.Add(Dish);
textBox1.Text =displayMembers(List);
}
string displayMembers(List<Food> vegetables)
{
foreach (Food s in vegetables)
{
return s.ToString();
}
return null;
Out put
TESTREADER.FOOD //TESTREADER IS A FOLDER WHERE TEST FILE IS.
You can do something like this:
public List<Food> getFoods()
{
List<Food> foods = new List<Food>();
using (var fileReader = new StreamReader(#"file.txt"))
{
var line = fileReader.ReadLine();
while (line != null)
{
string[] data = s.Split(';');
line = fileReader.ReadLine();
Food food = new Food
{
Name = data[0];
Group = data[1];
(...)
}
line = fileReader.ReadLine();
}
return foods;
}
Related
I have been binding short data to DataGridView in C# Winforms. However, I need to bind long string array with size 75 to DataGridView. My data list class consists of 6 individual variables with get and set and array of string which I have defined get and set properties. The individual variables are displayed but the array of strings is not displayed in DataGridView. In debug, I checked the data source of DataGridView and it seems ok. How can I display binded array in gridview.
Below is my source code to populate DataGridView named Logview
public void populateLogData(string path)
{
StreamReader sr = null;
BindingList<LogList> bindLogList;
BindingSource bLogsource = new BindingSource();
List<LogList> loglist = new List<LogList>();
try
{
Logview.DataSource = null;
Logview.Rows.Clear();
Logview.Columns.Clear();
Logview.AutoGenerateColumns = true;
if (File.Exists(path))
{
try
{
sr = new StreamReader(path);
StringBuilder readline = new StringBuilder(sr.ReadLine());
if (readline.ToString() != null && readline.ToString() != "")
{
readline = new StringBuilder(sr.ReadLine());
while (readline.ToString() != null && readline.ToString() != "")
{
string[] subdata = readline.ToString().Split(',');
LogList tloglist = new LogList(subdata[0], subdata[1], subdata[2], subdata[3], subdata[4], subdata[5], max_index);
for (int i = 6; i < subdata.Length; i++)
tloglist.setPartList(i-6, subdata[i]);
loglist.Add(new LogList(subdata, subdata.Length));
readline = new StringBuilder(sr.ReadLine());
}
}
bindLogList = new BindingList<LogList>(loglist);
bLogsource.DataSource = bindLogList;
Logview.AutoGenerateColumns = true;
Logview.DataSource = bindLogList;
Logview.Columns[0].Width = 140; // project name
Logview.Columns[1].Width = 140; // data/time
Logview.Columns[2].Width = 90;
Logview.Columns[3].Width = 90;
Logview.Columns[4].Width = 90;
Logview.Columns[5].Width = 90;
// max_index is set from another part of code
for(int i = 0; i <= max_index; i++)
{
int counter = 6 + i;
Logview.Columns.Add(headertext[i], headertext[i]);
Logview.Columns[counter].Width = 90;
Logview.Columns[counter].HeaderText = headertext[i];
}
}
catch (IOException io)
{
MessageBox.Show("Error: Cannot Open log file.");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
if (sr != null) sr.Close();
}
}
else
{
MessageBox.Show("Log file not found \n" + path);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
GC.Collect();
}
}
Below is LogList class
class LogList
{
const int max_size = 100;
private string[] holdList;
public string project { get; set; }
public string date_time { get; set; }
public string Qty { get; set; }
public string Pass { get; set; }
public string Fail { get; set; }
public string Result { get; set; }
public string[] partlist
{
get
{
return holdList;
}
set
{
holdList = value;
}
}
public LogList(string project, string date_time, string Qty, string Pass, string Fail, string Result, int partsize)
{
this.project = project;
this.date_time = date_time;
this.Qty = Qty;
this.Pass = Pass;
this.Fail = Fail;
this.Result = Result;
partlist = new string[partsize+1];
}
public void setPartList(int size, string getValue)
{
partlist[size] = getValue;
}
}
Project, date/time, Qty, Pass, Fail, Result is displayed. But partlist array is not displayed.
To supplement IVSoftware’s answer, below is an example using two grids in a master-detail scenario.
One issue I would have with your current approach, is that it uses an Array for the “parts list.” Currently this is a string array, and that isn’t going to work if we want to display it in a grid. Fortunately, there are a few easy ways we can get the data to display as we want.
One simple solution is to create a “wrapper” Class for the string. I will call this Class Part. I added a simple int ID property and the string PartName property. You could easily leave out the ID and have a simple string wrapper. This simple Class may look something like…
public class Part {
public int ID { get; set; }
public string PartName { get; set; }
}
This should allow the data to display correctly in the grid using just about any construct like an array, list etc.… So, we “could” change your current code to use an array of Part objects like…
Part[] Parts = new Parts[X];
And this would work, however, if we use an array and we know for sure that each LogItem may have a different number of parts in its PartsList, then we will have to manage the array sizes. So, a BindingList of Part objects will simplify this. The altered LogList (LogItem) Class is below…
public class LogItem {
public BindingList<Part> PartsList { get; set; }
public string Project { get; set; }
public string Date_Time { get; set; }
public string Qty { get; set; }
public string Pass { get; set; }
public string Fail { get; set; }
public string Result { get; set; }
public LogItem(string project, string date_Time, string qty, string pass, string fail, string result) {
Project = project;
Date_Time = date_Time;
Qty = qty;
Pass = pass;
Fail = fail;
Result = result;
PartsList = new BindingList<Part>();
}
}
So given the updated Classes, this should simplify things and we will use the same DataSource for both grids. This DataSource for the “master” grid will be a BindingList of LogItem objects. In the “detail” grid, we simply need to point it’s DataMember property to the PartsList property of the currently selected LogItem. And this would look something like…
dgvLogs.DataSource = LogsBL;
if (LogsBL.Count > 0) {
dgvParts.DataMember = "PartsList";
dgvParts.DataSource = LogsBL;
}
Below is the code to test the Classes above in a master-detail scenario with two grids. Create a new winform solution and drop two (2) DataGridViews on the form. The grid on the left is dgvLogs and the grid on the right is dgvParts.
public void populateLogData(string path) {
BindingList<LogItem> LogsBL = new BindingList<LogItem>();
string currentLine;
if (File.Exists(path)) {
try {
using (StreamReader sr = new StreamReader(path)) {
LogItem tempLogItem;
currentLine = sr.ReadLine(); // <- header row - ignoring
currentLine = sr.ReadLine();
while (currentLine != null) {
if (!string.IsNullOrEmpty(currentLine)) {
string[] splitArray = currentLine.Split(',');
if (splitArray.Length >= 6) {
tempLogItem = new LogItem(splitArray[0], splitArray[1], splitArray[2], splitArray[3], splitArray[4], splitArray[5]);
for (int i = 6; i < splitArray.Length; i++) {
tempLogItem.PartsList.Add(new Part { ID = i, PartName = splitArray[i] });
}
LogsBL.Add(tempLogItem);
}
else {
Debug.WriteLine("DataRead Error: Not enough items to make a LogItem: " + currentLine);
}
}
else {
Debug.WriteLine("DataRead Empty row");
}
currentLine = sr.ReadLine();
}
}
dgvLogs.DataSource = LogsBL;
if (LogsBL.Count > 0) {
dgvParts.DataMember = "PartsList";
dgvParts.DataSource = LogsBL;
}
}
catch (IOException io) {
MessageBox.Show("Error: Cannot Open log file.");
}
catch (Exception ex) {
MessageBox.Show(ex.Message + " Stacktrace- " + ex.StackTrace);
}
}
else {
MessageBox.Show("Log file not found \n" + path);
}
}
And some test data…
H1,h2,h3,h4,h5,h6,h7,h8
Model: LMG600N_IF_2blablas,2022-9-6,112,61,51,Fail,p1,p3,p4,p5,p6
1,2022-9-6,2112,621,251,Pass,px4,px5,px6,px1,px2,px3
data1,2022-9-7,3456,789,123,Fail,z3,z3,z4
Model: LMG600N_IF_2blablas,2022-9-6,112,61,51,Fail
Model: LMG600N_IF_2blablas,2022-9-6,112,61,51,Fail,p1,p3,p4,p5,p6,p7,p8,p99
BadData Model: LMG600N_IF_2blablas,2022-9-6,112,61
Moxxxdel: LMG600N_IF_2blablas,2022-9-6,11x2,6x1,5x1,Fail
Hope this helps and makes sense.
Your data list class consists of 6 individual variables with get and set, and an array of string. Your question is about the variables are displayed but the array of strings is not.
Here's what has worked for me (similar to the excellent suggestion by JohnG) for displaying the string array. What I'm doing here is taking a DataGridView and dropping in my main form without changing any settings (other than to Dock it). Given the default settings, the LogList class (shown here in a minimal reproducible example of 1 variable and 1 array of strings) is defined with a public string property named PartList and with this basic implementation:
class LogList
{
public LogList(string product, string[] partList)
{
Product = product;
_partList = partList;
}
public string Product { get; set; }
private string[] _partList;
public string PartList => string.Join(",", _partList);
}
To autoconfigure the DataGridView with Product and PartList columns, here is an example initializer method that sets the DataSource and adds the first three items as a test:
// Set data source property once. Clear it, Add to it, but no reason to nullify it.
BindingList<LogList> DataSource { get; } = new BindingList<LogList>();
private void InitDataGridView()
{
dataGridView1.DataSource = DataSource;
// Auto config columns by adding at least one Record.
DataSource.Add(
new LogList(
product: "LMG450",
// Four parts
partList: new string[]
{
"PCT2000",
"WCT100",
"ZEL-0812LN",
"EN61000-3-3/-11",
}
));
DataSource.Add(
new LogList(
product: "LMG600N",
// Three parts
partList: new string[]
{
"LTC2280",
"BMS6815",
"ZEL-0812LN",
}
));
DataSource.Add(
new LogList(
product: "Long Array",
// 75 parts
partList: Enumerable.Range(1, 75).Select(x => $"{ x }").ToArray()
));
// Use string indexer to access columns for formatting purposes.
dataGridView1
.Columns[nameof(LogList.Product)]
.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
dataGridView1
.Columns[nameof(LogList.PartList)]
.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
After running this code, the DGV looks like this:
With the mouse hovered over the item all 75 "parts" can be viewed.
One last thing - I notice you have some methods to assign a new partList[] of perhaps change an individual part at a specified index. (I didn't show them in the minimal sample but for sure you'll want things like that). You probably know this but make sure to call dataGridView1.Refresh after altering properties of an existing row/LogList object so that the view will reflect the changes.
I hope there's something here that offers a few ideas to achieve the outcome you want.
UML is attached. I want to create a readonly property of pre which is an array of string. When I create an object in the main and try to set name and pre it is showing me an error.
UML
using System;
class Unit
{
private string _name;
private string[] _pre;
public Unit(string name, string[] pre)
{
_name = name;
_pre = new string[2];
}
public string Name { get { return _name; } }
public string[] Pre { get { return _pre; } }
}
class Program
{
public static void DisplayInfo(Unit[] _u)
{
for (int i = 0; i < 2; i++)
{
Console.WriteLine(_u[i].Name + _u[i].Pre);
}
}
static void Main(string[] args)
{
Unit[] unitarraytest = new Unit[2];
unitarraytest[0] = new Unit("test 1", "test 3");
unitarraytest[1] = new Unit("test 2", "test 4");
DisplayInfo(unitarraytest);
}
}
Your example makes little sense. You Unit constructor takes a parameter for "Pre", but immediately throws it away and allocates a new empty string array instead. It should probably be written like
class Unit
{
public Unit(string name, string[] pre)
{
Name = name;
Pre = pre;
}
public string Name { get;}
public string[] Pre { get;}
}
When creating Unit objects you actually need to create an array for the "Pre" parameter. Like new Unit("Name", new []{"pre1", "pre2"});
And when outputting the strings you need to access the individual strings in the array, or combine them to a larger string, for example like Console.WriteLine(_u[i].Name + string.Join(" , ", _u[i].Pre));
I'm accessing a value object as follows: accounts[accountNum]
I want to do some minor manipulation on this object, ie: increase it by something, etc. Rather than typing "accounts[accountNum] += something" I'd rather do something like this:
MemberData account = accounts[accountNum];
account.balance += something;
Ie: "account" should be a direct pointer, reference, or somehow "be equivalent to" typing "accounts[accountNum]". But since this is a value type and not a reference type, it makes a copy and I cannot manipulate that object directly.
All my research on using "ref" seems to talk about using it within method parameters and return types, not in variables. Is there a simple way to shortcut a long variable name whilst still referencing the same object?
UPDATE:
Full source code - Please see specifically Line 46 & 51
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
/* * Description of the project:
* To implement an ATM machine which accepts deposits, withdrawals
* DEPOSIT / WITHDRAWAL:
* Account, Amount, Timestamp, TransactionID, Location
* Keep record of all accounts in TXT file, which is updated after each transaction. Of course in live system DB would be used.
* Keep another file that logs all transactions. Make it possible to LINQ the log associated with account, to show transaction history.
* TXT Accounts in form of:
* Account #, Name, Balance
* */
namespace ATMProject
{
class Program
{
static void Main(string[] args)
{
// Globals.DisplayAccounts();
// Deposit.MakeDeposit(1000009, 500, "sydney");
}
}
static class Deposit
{
//TODO Deposit: * Account #, Amount, Timestamp, TransactionID, Location
public static void MakeDeposit(long accountNum, long amount, string location)
{
string timestamp;
string transactionID;
timestamp = DateTime.Now.ToString("yyyyMMddhhmm");
transactionID = Globals.GenerateValidTransactionID();
using (StreamWriter sr = new StreamWriter(Globals.transactions, true))
{
string transactString = $"DEPOSIT| {accountNum}| {amount}| {timestamp}| {transactionID}| {location}";
sr.WriteLine(transactString);
}
Globals.WriteTransactionID(transactionID);
Dictionary<long, MemberData> accounts = Globals.GenerateAccounts();
try
{
//WORKS
MemberData account = accounts[accountNum];
account.balance += amount;
accounts[accountNum] = account;
//DOESN'T BUILD
//ref MemberData account = ref accounts[accountNum];
}
catch (KeyNotFoundException)
{
Console.WriteLine("Deposit failed, selected account number does not exist in accounts.txt");
}
}
}
static class Withdraw
{
}
static class CreateAccount
{
public static void Create(string name, long StartDeposit)
{
long accountNumber;
using (StreamReader sr = new StreamReader(Globals.accountIter))
{
accountNumber = Convert.ToInt64(sr.ReadLine());
sr.Close();
}
using (StreamWriter sr = new StreamWriter(Globals.accounts, true))
{
string accountAdd = $"{accountNumber}| {name}| {StartDeposit}";
sr.WriteLine(accountAdd);
Globals.SetNextAccountNumber();
Console.WriteLine("Successfully created account #" + accountNumber);
sr.Close();
}
}
}
struct MemberData
{
public long accountNum;
public string name;
public long balance;
public MemberData(long accountNum, string name, long balance)
{
this.accountNum = accountNum;
this.name = name;
this.balance = balance;
}
public override string ToString()
{
string thisStr = $"Account #: {accountNum}, Name: {name}, Balance: {balance}";
return thisStr;
}
}
static class Globals
{
public static string basepath = "C:\\Users\\root\\RiderProjects\\ConsoleApp1\\ATMProject\\";
public static string accounts = basepath + "accounts.txt"; //path to all accounts
public static string transactions = basepath + "transactions.txt"; //path to all accounts
public static string accountIter = basepath + "accountIter.txt"; //path to the current account iter
public static string transactIter = basepath + "transactIter.txt"; //path to the current account iter
//class values
public static long GetNextAccountNumber() //returns the next available account #
{
try
{
using (StreamReader sr = new StreamReader(accountIter))
{
long currAccountIter = Convert.ToInt64(sr.ReadLine());
sr.Close();
return currAccountIter;
}
}
catch (IOException e)
{
Console.WriteLine(e.Message);
return -1;
}
}
public static void SetNextAccountNumber() //increments the account #
{
long currAccountIter = GetNextAccountNumber();
if (currAccountIter == -1)
{
Console.WriteLine("Could not SetNextAccountNumber");
return;
}
using (StreamWriter sr = new StreamWriter(accountIter, false))
{
currAccountIter += 1;
sr.WriteLine(currAccountIter);
sr.Close();
}
}
public static Dictionary<long, MemberData> GenerateAccounts() //Returns Dictionary in form of <Account Number, MemberData>
{
Dictionary<long, MemberData> memberList = new Dictionary<long, MemberData>();
using (StreamReader sr = new StreamReader(accounts))
{
while (!sr.EndOfStream)
{
var memberSplit = sr.ReadLine().Split("| ", StringSplitOptions.RemoveEmptyEntries);
long accountNum = Convert.ToInt64(memberSplit[0]);
string name = memberSplit[1];
long balance = Convert.ToInt64(memberSplit[2]);
MemberData member = new MemberData(accountNum, name, balance);
memberList.Add(accountNum, member);
}
}
return memberList;
}
public static void DisplayAccounts()
{
Console.WriteLine("List of all Accounts:\n===========");
foreach (var member in GenerateAccounts())
{
Console.WriteLine(member.Value);
}
}
public static string GetTimestamp()
{
string timestamp = DateTime.Now.ToString("yyyyMMddhhmmss");
return timestamp;
}
private static string GetTransactionIter()
{
using (StreamReader sr = new StreamReader(transactIter))
{
string transactionID = sr.ReadLine();
sr.Close();
return transactionID;
}
}
public static string GenerateValidTransactionID()
{
string IDPart;
string timestamp = GetTimestamp();
string transactionID = GetTransactionIter();
string numberSlice = transactionID.Substring(14);
string timestampSlice = transactionID.Substring(0, 14);
if (timestamp == timestampSlice)
{
IDPart = Convert.ToString(Convert.ToInt64(numberSlice) + 1);
transactionID = timestamp + IDPart;
}
else
{
transactionID = timestamp + 0;
}
return transactionID;
}
public static void WriteTransactionID(string transactionID) //overwrite and write current transactionID to iter
{
using (StreamWriter sr = new StreamWriter(transactIter, false))
{
sr.WriteLine(transactionID);
sr.Close();
}
}
public static void GenereateAndWriteTransactionID()
{
WriteTransactionID(GenerateValidTransactionID());
}
}
}
Since C# 7 you can use local ref variables:
ref int niceName = ref localVariable.Values.SubArray.Here;
niceName += 20;
Note that you can not use this in async methods. And it does neither work with properties nor indexers (except for arrays).
I have a text file that contains a list of points:
POINT:
TYPE 5,
OBJECT ID 2,
DEVICE TYPE CAT,
TAG 'ADDRESS-1',
DESCRIPTION 'kitty',
UNITS 'Lb',
POINT:
TYPE 5,
OBJECT ID 2,
DEVICE TYPE CAT,
TAG 'ADDRESS-2',
DESCRIPTION 'orange kitty',
UNITS 'Lb',
POINT:
TYPE 2,
OBJECT ID 3,
DEVICE TYPE DOG,
TAG 'ADDRESS-5',
DESCRIPTION 'brown dog',
UNITS 'Lb',
From this, I want to create instances (in this case, 2) of my class 'Cat' that contain the tag and description in this text file(and then put them in a list of Cats). I only want to take the description and tag from points of Type 5 (those are the cats).
I'm not sure what the best approach is to get the strings I want. I need to search the entire file for all points of type 5, then for each of those points, take the description and tag and add it to a new Cat.
public static void Main()
{
string line;
List<Cat> catList = new List<Cat>();
StreamReader file = new StreamReader(#"C:\Config\pets.txt");
while((line = file.ReadLine()) != null)
{
string[] words = line.Split(',');
catList.Add(new Cat cat1)
}}
I ended up doing it this way:
public static List<List<string>> Parse()
{
string filePath = #"C:\Config\pets.txt";
string readText = File.ReadAllText(filePath);
string[] stringSeparators = new string[] { "POINT:" }; //POINT is the keyword the text will be split on
string[] result;
result = readText.Split(stringSeparators, StringSplitOptions.None);
List<List<string>> catData = new List<List<string>>();
//split the text into an list of pieces
List<string> tags = new List<string>(); //tags go here
List<string> descriptions = new List<string>(); //descriptions go here
foreach (string s in result)
{
if (s.Contains("TYPE 5")) //TYPE 5 = CAT
{
string[] parts = s.Split(','); //split the cat by commas
string chop = "'"; //once tags and descriptions have been found, only want to keep what is inside single quotes ie 'orange kitty'
foreach (string part in parts)
{
if (part.Contains("TAG"))
{
int startIndex = part.IndexOf(chop);
int endIndex = part.LastIndexOf(chop);
int length = endIndex - startIndex + 1;
string path = part.Substring(startIndex, length);
tag = tag.Replace(chop, string.Empty);
tags.Add(tag);
//need to create instance of Cat with this tag
}
if (part.Contains("DESCRIPTION"))
{
int startIndex = part.IndexOf(chop);
int endIndex = part.LastIndexOf(chop);
int length = endIndex - startIndex + 1;
string description = part.Substring(startIndex, length);
description = description.Replace(chop, string.Empty);
descriptions.Add(description);
//need to add description to Cat instance that matches associated tag
}
}
}
}
catData.Add(tags);
catData.Add(descriptions);
return catData;
What I would do is create a class that represents the fields you want to capture. For this example, I'm capturing all the fields, but you can customize it how you want. I called this class "Animal", since it seems the "points" represent an animal.
Then I would add a static Parse method to the class that will return an instance of an Animal based on some input string. This method will parse the input string and attempt to set the relevant properties of the Animal object based on the values in the string.
I also added a ToString() override on the class so we have some way of displaying the output:
class Animal
{
public int Type { get; set; }
public int ObjectId { get; set; }
public string DeviceType { get; set; }
public string Tag { get; set; }
public string Description { get; set; }
public string Units { get; set; }
/// <summary>
/// Parses an input string and returns an Animal based
/// on any property values found in the string
/// </summary>
/// <param name="input">The string to parse for property values</param>
/// <returns>An animal instance with specified properties</returns>
public static Animal Parse(string input)
{
var result = new Animal();
if (string.IsNullOrWhiteSpace(input)) return result;
// Parse input string and set fields accordingly
var keyValueParts = input
.Split(new [] {','}, StringSplitOptions.RemoveEmptyEntries)
.Select(kvp => kvp.Trim());
foreach (var keyValuePart in keyValueParts)
{
if (keyValuePart.StartsWith("Type",
StringComparison.OrdinalIgnoreCase))
{
int type;
var value = keyValuePart.Substring("Type".Length).Trim();
if (int.TryParse(value, out type))
{
result.Type = type;
}
}
else if (keyValuePart.StartsWith("Object Id",
StringComparison.OrdinalIgnoreCase))
{
int objectId;
var value = keyValuePart.Substring("Object Id".Length).Trim();
if (int.TryParse(value, out objectId))
{
result.ObjectId = objectId;
}
}
else if (keyValuePart.StartsWith("Device Type",
StringComparison.OrdinalIgnoreCase))
{
var value = keyValuePart.Substring("Device Type".Length).Trim();
result.DeviceType = value;
}
else if (keyValuePart.StartsWith("Tag",
StringComparison.OrdinalIgnoreCase))
{
var value = keyValuePart.Substring("Tag".Length).Trim();
result.Tag = value;
}
else if (keyValuePart.StartsWith("Description",
StringComparison.OrdinalIgnoreCase))
{
var value = keyValuePart.Substring("Description".Length).Trim();
result.Description = value;
}
else if (keyValuePart.StartsWith("Units",
StringComparison.OrdinalIgnoreCase))
{
var value = keyValuePart.Substring("Units".Length).Trim();
result.Units = value;
}
}
return result;
}
public override string ToString()
{
// Return a string that describes this animal
var animalProperties = new StringBuilder();
animalProperties.Append($"Type = {Type}, Object Id = {ObjectId}, ");
animalProperties.Append($"Device Type = {DeviceType}, Tag = {Tag}, ");
animalProperties.Append($"Description = {Description}, Units = {Units}");
return animalProperties.ToString();
}
}
Now that we have an object that can create itself from a string, we just need to read in the file contents, split it on the "Point:" keyword, and then pass each string to the Animal class to get our instances.
I added some System.Linq clause to filter on animals that have Type = 5 (which you said are all cats), since you said that was the only animal you were interested in. Of course you could remove this to get all animals, or replace it with "Dog" to get the dogs, etc..:
private static void Main()
{
var filePath = #"f:\public\temp\temp.txt";
// Read all file contents and split it on the word "Point:"
var fileContents = Regex
.Split(File.ReadAllText(filePath), "Point:", RegexOptions.IgnoreCase)
.Where(point => !string.IsNullOrWhiteSpace(point))
.Select(point => point.Trim());
// Get all animals that are cats from the results
var catList = fileContents
.Select(Animal.Parse)
.Where(animal => animal.Type == 5)
.ToList();
// Output results
catList.ForEach(Console.WriteLine);
// Wait for input before closing
Console.WriteLine("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
Output
I am relatively new to C# (WinForms), and had a question regarding combo boxes. I have a combo box of Reviewer objects (it is a custom class with an overridden ToString method) and am currently attempting to go through all the checked items and use them to generate a setup file.
Here is how the combo box is populated (populated on form load). Parameters is just a collection of linked lists and parsing code.
for (int i = 0; i < parameters.GetUsers().Count; i++)
{
UserList.Items.Add(parameters.GetUsersArray()[i], parameters.GetUsersArray()[i].isSelected());
}
Here is how I am trying to read it. setup is a StringBuilder. The problem is that GetID is not defined. Does the add function above cast the Reviewer object to a Object object? It looks a little funny since it creates a file fed into a Perl script. A sample desired output line looks like this: inspector0 => "chg0306",
for (int i = 0; i < UserList.CheckedItems.Count; i++)
{
setup.AppendLine("inspector" + i.ToString() + " => \t \"" +
UserList.CheckedItems[i].GetID() + "\",");
}
Here is the users class: (Sample User is ID = aaa0000 name: Bob Joe)
public class Reviewer
{
private string name;
private string id;
private bool selected;
public Reviewer(string newName, string newID, bool newSelected)
{
name = newName;
id = newID;
selected = newSelected;
}
public string GetName()
{
return name;
}
public override string ToString()
{
//string retVal = new string(' ', id.Length + name.Length + 1);
string retVal = id + '\t' + name;
return retVal;
}
public string GetID()
{
return id;
}
public bool isSelected()
{
return selected;
}
}
For posterity, here is the Parameters class:
public class ParameterLists
{
public ParameterLists()
{
projects = new LinkedList<string>();
reviewers = new LinkedList<Reviewer>();
}
public enum FileContents {
PROJECT_LIST,
USERS_LIST,
}
public LinkedList<Reviewer> GetUsers()
{
return reviewers;
}
public LinkedList<string> GetProjects()
{
return projects;
}
public Reviewer[] GetUsersArray()
{
Reviewer[] userArray = new Reviewer[reviewers.Count];
reviewers.CopyTo(userArray, 0);
return userArray;
}
public string[] GetProjectsArray()
{
String[] projectArray = new String[projects.Count];
projects.CopyTo(projectArray, 0);
return projectArray;
}
public void LoadParameters(string fileName)
{
//Reads the parameters from the input file.
}
private void CreateDefaultFile(string fileName)
{
// Create the file from the defaultfile , if it exists.
// Otherwise create a blank default file.
}
private LinkedList <string> projects;
private LinkedList <Reviewer> reviewers;
}
I am probably missing something simple, coming from embedded C++. Any help would be appreciated.
You have to cast that object:
((Reviewer)UserList.CheckedItems[i]).GetID()