Using one array to track 3 pieces of data - c#

I need your help with using one array to track 3 pieces of data. I have o use one array because it is school assignment and I am required to do so.
The array has to track Province ID, Region ID and the population for each region. There is 13 province and in each province there is 48 regions. Since the array is fixed size, I used a 624 by 3 multi dimensional array. The first column is for the provinceID, the second is for the regionID, and the third one is for the population.. I set the rows to 624 because there is 13 province and 48 regions in each province so 13*48 = 624.
I was able to insert my data and display them like this
ProvinceID # 1 RegionID # 1: 125000
Instead I would like to display the regions and population by province.
Something like this:
ProvinceID #1
RegionID # 1: 12000
RegionID # 2: 30000
ProvinceID #2
RegionID #1: 14000
RegionID #: 145000
Here is what I did
I declare a global array
int[,] census;
I initialize it on form initialize
census = new int[624,3];
Here is my insert
try
{
// declare variables
int prov, region, population;
prov = Convert.ToInt32(txtProvinceID.Text);
region = Convert.ToInt32(txtRegionID.Text);
population = Convert.ToInt32(txtPopulation.Text);
census[counter, 0] = prov;
census[counter, 1] = region;
census[counter, 2] = population;
counter++;
MessageBox.Show("Population " + txtPopulation.Text.ToString() + " saved for Province #" + txtProvinceID.Text.ToString()
+ " , Region #" + txtRegionID.Text.ToString(), "Success!");
txtRegionID.Clear();
txtProvinceID.Clear();
txtPopulation.Clear();
txtProvinceID.Focus();
}
catch (Exception ex)
{
if (ex.Message == "Input string was not in a correct format.")
{
MessageBox.Show("Please enter a numeric value", "Error");
}
else
{
MessageBox.Show(ex.Message, "Error");
}
}
This is the code to retrieve the data and save them to a file
string output = "";
try
{
for (int rows = 0; rows < census.GetLength(0); rows++)
{
for (int columns = 0; columns < census.GetLength(1); columns++)
{
if (census[rows, columns] != 0)
{
if (columns == 0)
{
output += "Province ID #" + census[rows, columns];
}
else if (columns == 1)
{
output += "Region ID #" + census[rows, columns] + ": ";
}
else if (columns == 2)
{
output += census[rows, columns] + "\n";
}
}// END if census[rows, coloumns]!=0
}// END for coloumns
}//END for(int row =0
// save the data to a text file
SaveFileDialog sfd = new SaveFileDialog();
sfd.FileName = "untitled";
sfd.Filter = "Text (*.txt)|*.txt|Word Doc (*.doc)|*.doc";
sfd.DefaultExt = "txt";
sfd.AddExtension = true;
sfd.ShowDialog();
FileStream sf = new FileStream(sfd.FileName, FileMode.Create);
StreamWriter sw = new StreamWriter(sf);
sw.Write(output);
sw.Close();
}
catch (Exception)
{
}

Think of a table in which the rows are provinces and the columns are regions. And in each table cell is the population for that province/region. It would look like this
Region 1 Region 2 Region 3
Province 1 12000 13000 14000
Province 2 11320 9876 1234
Province 3 19723 32767 5038
Province 4 263 1284 1961
This exactly corresponds to a two-dimensional array:
int[,] census = new int[4,3];
And the values in the array would be, for example:
census[0,0] = 12000; // Province 1, Region 1
census[0,1] = 13000; // Province 1, Region 2
census[2,2] = 5038; // Province 3, Region 3
Modifying your code, you'd create your census array as:
int[,] census = new int[13, 48];
And to populate it:
prov = Convert.ToInt32(txtProvinceID.Text);
region = Convert.ToInt32(txtRegionID.Text);
population = Convert.ToInt32(txtPopulation.Text);
census[prov-1, region-1] = population;
Now, if you want to output by province and region:
StringBuilder output = new StringBuilder();
for (int prov = 0; prov < census.GetLength(0); prov++)
{
output.AppendLine("Province ID " + prov+1);
for (int region = 0; region < census.GetLength(1); ++region)
{
output.AppendLine(" Region #" + region+1 + " " +
census[prov, region]);
}
}
And then to output it, you open the file as you showed and Write(output.ToString()).
I used StringBuilder to construct the output rather than appending to a string. Appending strings is very expensive.
Also, you can simplify your file writing you using File.WriteAllText, like this:
// Show the Save File dialog box. And then,
File.WriteAllText(sfd.Filename, output.ToString());
There's no need to open a FileStream and then a StreamWriter. File.WriteAllText takes care of all that for you.
Update
To skip provinces that have no data, you write to a temporary StringBuilder and then append that to the final output only if there is data. For example:
StringBuilder output = new StringBuilder();
for (int prov = 0; prov < census.GetLength(0); prov++)
{
StringBuilder sbTemp = new StringBuilder();
for (int region = 0; region < census.GetLength(1); ++region)
{
if (census[prov, region] != 0)
{
sbTemp.AppendLine(" Region #" + region+1 + " " +
census[prov, region]);
}
}
if (sbTemp.Length > 0)
{
// At least one region had population, so add that
// and the province to the output.
output.AppendLine("Province ID " + prov+1);
output.Append(sbTemp);
}
}

The array has to track Province ID, Region ID and the population for each region.
This indicates that you should create a class to hold the three pieces of data. This way, it will be much easier to work with the array. Something like:
class Region
{
public int ProvinceId { get; set; }
public int RegionId { get; set; }
public long Population { get; set; }
}
Notice that I chose a different type for population, you wouldn't be able to do this with a simple array and you're certainly going to need something like that sooner or later.
When you have that, you can use LINQ to create groups of regions from the same province:
Region[] regions = …; // or maybe List<Region>
var regionGroups = regions.GroupBy(r => r.ProvinceId);
var result = new StringBuilder();
foreach (regionGroup in regionGroups)
{
result.AppendFormat("Province ID #{0}\n\n", regionGroup.Key);
foreach (var region in regionGroup)
{
result.AppendFormat(
"Region ID #{0}: {1}\n", region.RegionId, region.Population);
}
}
return result.ToString();
Also, you should never ever do this:
catch (Exception)
{
}
If you need to handle some specific exceptions, handle those, but only those.

I think it is really difficult to do this with your current program design
i suggest you to use jagged arrays for storing the values since LINQ can not be used on 2D arrays
A jagged array is an "array of arrays"
string[][] census=new string[624][];
for(int i=0;i<624;i++)
{
census[i]=new string[3];
}
Now you can use LINQ to get the required data from the array
var match=from c in census
where c[0]=="Province ID Text"
from details in c
where details!="Province ID Text"
select details;
and print the values as follows
foreach(var i in match)
Console.WriteLine(i);

If you are really particular on NOT using objects or database tables to store the values and
also don't want to use LINQ, the following solution might help you
void Select(string key)
{
for(int i=0;i<624;i++)
{
if(census[i][0]==key)
{
Console.Write(census[i][1]);
Console.Write(census[i][2]);
Console.WriteLine();
}
}
}
To eliminate duplicate data, we use a List to check whether that Province Id was previously checked or not
string done = "";
for (int i = 0; i < 624; i++)
{
if (!done.Contains(census[i][0]))
{
Console.WriteLine(census[i][0]);
Select(census[i][0]);
}
done=done+census[i][0];
}
I hope this code helps you but this is really NOT a good way to program.

Related

Parse a multiline email to var

I'm attempting to parse a multi-line email so I can get at the data which is on its own newline under the heading in the body of the email.
It looks like this:
EMAIL STARTING IN APRIL
Marketing ID Local Number
------------------- ----------------------
GR332230 0000232323
Dispatch Code Logic code
----------------- -------------------
GX3472 1
Destination ID Destination details
----------------- -------------------
3411144
It appears I am getting everything on each messagebox when I use string reader readline, though all I want is the data under each ------ as shown
This is my code:
foreach (MailItem mail in publicFolder.Items)
{
if (mail != null)
{
if (mail is MailItem)
{
MessageBox.Show(mail.Body, "MailItem body");
// Creates new StringReader instance from System.IO
using (StringReader reader = new StringReader(mail.Body))
{
string line;
while ((line = reader.ReadLine()) !=null)
//Loop over the lines in the string.
if (mail.Body.Contains("Marketing ID"))
{
// var localno = mail.Body.Substring(247,15);//not correct approach
// MessageBox.Show(localrefno);
//MessageBox.Show("found");
//var conexid = mail.Body.Replace(Environment.NewLine);
var regex = new Regex("<br/>", RegexOptions.Singleline);
MessageBox.Show(line.ToString());
}
}
//var stringBuilder = new StringBuilder();
//foreach (var s in mail.Body.Split(' '))
//{
// stringBuilder.Append(s).AppendLine();
//}
//MessageBox.Show(stringBuilder.ToString());
}
else
{
MessageBox.Show("Nothing found for MailItem");
}
}
}
You can see I had numerous attempts with it, even using substring position and using regex. Please help me get the data from each line under the ---.
It is not a very good idea to do that with Regex because it is quite easy to forget the edge cases, not easy to understand, and not easy to debug. It's quite easy to get into a situation that the Regex hangs your CPU and times out. (I cannot make any comment to other answers yet. So, please check at least my other two cases before you pick your final solution.)
In your cases, the following Regex solution works for your provided example. However, some additional limitations are there: You need to make sure there are no empty values in the non-starting or non-ending column. Or, let's say if there are more than two columns and any one of them in the middle is empty will make the names and values of that line mismatched.
Unfortunately, I cannot give you a non-Regex solution because I don't know the spec, e.g.: Will there be empty spaces? Will there be TABs? Does each field has a fixed count of characters or will they be flexible? If it is flexible and can have empty values, what kind of rules to detected which columns are empty? I assume that it is quite possible that they are defined by the column name's length and will have only space as delimiter. If that's the case, there are two ways to solve it, two-pass Regex or write your own parser. If all the fields has fixed length, it would be even more easier to do: Just using the substring to cut the lines and then trim them.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
public class Program
{
public class Record{
public string Name {get;set;}
public string Value {get;set;}
}
public static void Main()
{
var regex = new Regex(#"(?<name>((?!-)[\w]+[ ]?)*)(?>(?>[ \t]+)?(?<name>((?!-)[\w]+[ ]?)+)?)+(?:\r\n|\r|\n)(?>(?<splitters>(-+))(?>[ \t]+)?)+(?:\r\n|\r|\n)(?<value>((?!-)[\w]+[ ]?)*)(?>(?>[ \t]+)?(?<value>((?!-)[\w]+[ ]?)+)?)+", RegexOptions.Compiled);
var testingValue =
#"EMAIL STARTING IN APRIL
Marketing ID Local Number
------------------- ----------------------
GR332230 0000232323
Dispatch Code Logic code
----------------- -------------------
GX3472 1
Destination ID Destination details
----------------- -------------------
3411144";
var matches = regex.Matches(testingValue);
var rows = (
from match in matches.OfType<Match>()
let row = (
from grp in match.Groups.OfType<Group>()
select new {grp.Name, Captures = grp.Captures.OfType<Capture>().ToList()}
).ToDictionary(item=>item.Name, item=>item.Captures.OfType<Capture>().ToList())
let names = row.ContainsKey("name")? row["name"] : null
let splitters = row.ContainsKey("splitters")? row["splitters"] : null
let values = row.ContainsKey("value")? row["value"] : null
where names != null && splitters != null &&
names.Count == splitters.Count &&
(values==null || values.Count <= splitters.Count)
select new {Names = names, Values = values}
);
var records = new List<Record>();
foreach(var row in rows)
{
for(int i=0; i< row.Names.Count; i++)
{
records.Add(new Record{Name=row.Names[i].Value, Value=i < row.Values.Count ? row.Values[i].Value : ""});
}
}
foreach(var record in records)
{
Console.WriteLine(record.Name + " = " + record.Value);
}
}
}
output:
Marketing ID = GR332230
Local Number = 0000232323
Dispatch Code = GX3472
Logic code = 1
Destination ID = 3411144
Destination details =
Please note that this also works for this kind of message:
EMAIL STARTING IN APRIL
Marketing ID Local Number
------------------- ----------------------
GR332230 0000232323
Dispatch Code Logic code
----------------- -------------------
GX3472 1
Destination ID Destination details
----------------- -------------------
3411144
output:
Marketing ID = GR332230
Local Number = 0000232323
Dispatch Code = GX3472
Logic code = 1
Destination ID =
Destination details = 3411144
Or this:
EMAIL STARTING IN APRIL
Marketing ID Local Number
------------------- ----------------------
Dispatch Code Logic code
----------------- -------------------
GX3472 1
Destination ID Destination details
----------------- -------------------
3411144
output:
Marketing ID =
Local Number =
Dispatch Code = GX3472
Logic code = 1
Destination ID =
Destination details = 3411144
var dict = new Dictionary<string, string>();
try
{
var lines = email.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
int starts = 0, end = 0, length = 0;
while (!lines[starts + 1].StartsWith("-")) starts++;
for (int i = starts + 1; i < lines.Length; i += 3)
{
var mc = Regex.Matches(lines[i], #"(?:^| )-");
foreach (Match m in mc)
{
int start = m.Value.StartsWith(" ") ? m.Index + 1 : m.Index;
end = start;
while (lines[i][end++] == '-' && end < lines[i].Length - 1) ;
length = Math.Min(end - start, lines[i - 1].Length - start);
string key = length > 0 ? lines[i - 1].Substring(start, length).Trim() : "";
end = start;
while (lines[i][end++] == '-' && end < lines[i].Length) ;
length = Math.Min(end - start, lines[i + 1].Length - start);
string value = length > 0 ? lines[i + 1].Substring(start, length).Trim() : "";
dict.Add(key, value);
}
}
}
catch (Exception ex)
{
throw new Exception("Email is not in correct format");
}
Live Demo
Using Regular Expressions:
var dict = new Dictionary<string, string>();
try
{
var lines = email.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
int starts = 0;
while (!lines[starts + 1].StartsWith("-")) starts++;
for (int i = starts + 1; i < lines.Length; i += 3)
{
var keys = Regex.Matches(lines[i - 1], #"(?:^| )(\w+\s?)+");
var values = Regex.Matches(lines[i + 1], #"(?:^| )(\w+\s?)+");
if (keys.Count == values.Count)
for (int j = 0; j < keys.Count; j++)
dict.Add(keys[j].Value.Trim(), values[j].Value.Trim());
else // remove bug if value of first key in a line has no value
{
if (lines[i + 1].StartsWith(" "))
{
dict.Add(keys[0].Value.Trim(), "");
dict.Add(keys[1].Value.Trim(), values[0].Value.Trim());
}
else
{
dict.Add(keys[0].Value, values[0].Value.Trim());
dict.Add(keys[1].Value.Trim(), "");
}
}
}
}
catch (Exception ex)
{
throw new Exception("Email is not in correct format");
}
Live Demo
Here is my attempt. I don't know if the email format can change (rows, columns, etc).
I can't think of an easy way to separate the columns besides checking for a double space (my solution).
class Program
{
static void Main(string[] args)
{
var emailBody = GetEmail();
using (var reader = new StringReader(emailBody))
{
var lines = new List<string>();
const int startingRow = 2; // Starting line to read from (start at Marketing ID line)
const int sectionItems = 4; // Header row (ex. Marketing ID & Local Number Line) + Dash Row + Value Row + New Line
// Add all lines to a list
string line = "";
while ((line = reader.ReadLine()) != null)
{
lines.Add(line.Trim()); // Add each line to the list and remove any leading or trailing spaces
}
for (var i = startingRow; i < lines.Count; i += sectionItems)
{
var currentLine = lines[i];
var indexToBeginSeparatingColumns = currentLine.IndexOf(" "); // The first time we see double spaces, we will use as the column delimiter, not the best solution but should work
var header1 = currentLine.Substring(0, indexToBeginSeparatingColumns);
var header2 = currentLine.Substring(indexToBeginSeparatingColumns, currentLine.Length - indexToBeginSeparatingColumns).Trim();
currentLine = lines[i+2]; //Skip dash line
indexToBeginSeparatingColumns = currentLine.IndexOf(" ");
string value1 = "", value2 = "";
if (indexToBeginSeparatingColumns == -1) // Use case of there being no value in the 2nd column, could be better
{
value1 = currentLine.Trim();
}
else
{
value1 = currentLine.Substring(0, indexToBeginSeparatingColumns);
value2 = currentLine.Substring(indexToBeginSeparatingColumns, currentLine.Length - indexToBeginSeparatingColumns).Trim();
}
Console.WriteLine(string.Format("{0},{1},{2},{3}", header1, value1, header2, value2));
}
}
}
static string GetEmail()
{
return #"EMAIL STARTING IN APRIL
Marketing ID Local Number
------------------- ----------------------
GR332230 0000232323
Dispatch Code Logic code
----------------- -------------------
GX3472 1
Destination ID Destination details
----------------- -------------------
3411144";
}
}
Output looks something like this:
Marketing ID,GR332230,Local Number,0000232323
Dispatch Code,GX3472,Logic code,1
Destination ID,3411144,Destination details,
Here is an aproach asuming you don't need the headers, info comes in order and mandatory.
This won't work for data that has spaces or optional fields.
foreach (MailItem mail in publicFolder.Items)
{
MessageBox.Show(mail.Body, "MailItem body");
// Split by line, remove dash lines.
var data = Regex.Split(mail.Body, #"\r?\n|\r")
.Where(l => !l.StartsWith('-'))
.ToList();
// Remove headers
for(var i = data.Count -2; lines >= 0; i -2)
{
data.RemoveAt(i);
}
// now data contains only the info you want in the order it was presented.
// Asuming info doesn't have spaces.
var result = data.SelectMany(d => d.Split(' '));
// WARNING: Missing info will not be present.
// {"GR332230", "0000232323", "GX3472", "1", "3411144"}
}

c# creating multiple lists within for statement

Is there a way I can create multiple lists under a "for" statement, with an incrimental naming system (eg monster1, monster2, monster3). I've flagged it up under this is where I want an incrimental name within the code.
The number of lists needed to be generated is defined earlier on and I'm having no issues with that, but I'm getting "error CS0128: A local variable named `...' is already defined in this scope" if I attempt to assign the list name based on a variable with an inbuilt counter to incriment the list names (monsterList in this case).
Is there another way I can do this, so that no matter how many lists I need to be generated, it will create them and name them following this set pattern?
The below code is what I'm looking to have iterate a nuber of times (I'm new, so it's probably horribly inefficient, I'm just trying to get it doing what I want for now!):
for (int monstCounter = 2; monstCounter < totalTimes; monstCounter++)
{
Console.WriteLine();
string monsterLists = "Monster " + monstCounter;
string monsterList = "monsters"+monstCounter;
Console.WriteLine(monsterLists);
p = 0;
foreach (Monster aMonster in monsters)
{
if (monsters[p].MonsterName == monsterLists)
y = p;
p++;
}
y = monsters[y].MonsterPopu;
//Create a list of individuals.
List<MonsterStatistics> **This is where I want an incrimental name** = new List<MonsterStatistics>();
totalTimes1 = y;
counter1 = 1;
for (int b=0; b < totalTimes1; b++)
{
// Add monsters to the list.
monsterList.Add(new MonsterStatistics() {oldAge = rndNum.Next(1,100), name = "Name"+counter1, coupled = false, genderGen = rndNum.Next(0,2)});
counter1++;
}
foreach (MonsterStatistics aMonster in monsterList){
if(aMonster.genderGen == 0)
aMonster.gender = "Male";
else if (aMonster.genderGen == 1)
aMonster.gender = "Female";
else
aMonster.gender = "Genderless";
aMonster.age = rndNum.Next(0,aMonster.oldAge);
Console.WriteLine(aMonster.name + " Age: " + aMonster.age + "/" + aMonster.oldAge + " " + aMonster.gender);
}
}
You can't create variables from a string, its not how variables work in C#.
What you cand o is use a Dictionary and assign for each monsters list a unique key (string), something like:
Dictionary<string, List<MonsterStatistics>> monstersStatistics = new Dictionary<string, List<MonsterStatistics>>();
// Add a monster (here you can name monster with an incrementing variable)
monstersStatistics.Add("monster1", new List<MonsterStatistics>());
// Then fill the monster's statistics
monstersStatistics["monster1"].Add(new MonsterStatistics() { oldAge = rndNum.Next(1, 100), name = "Name" + counter1, coupled = false, genderGen = rndNum.Next(0, 2) });

How to read file that contains one row with multiple records- C# [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I have this text file that only has one row. Each file contains one customer name but multiple items and descriptions.
Record starting with 00 (Company Name) has a char length of 10
01 (Item#) - char length of 10
02 (Description) - char length of 50
I know how to read a file, but I don't have any idea of how to loop through only one line, find records 00, 01, 02 and grab the text based on the length, finally start at the position of the last records and start the loop again. Can someone please give me an idea of how to read files like this?
output:
companyName 16622 Description
companyName 15522 Description
input text file example
00Init 0115522 02Description 0116622 02Description
This solution assumes that the data is fixed width, and that item number will preceed description (01 before 02). This solution will emit a record every time a description record is encountered, and deals with multiple products for the same company.
First, define a class to hold your data:
public class Record
{
public string CompanyName { get; set; }
public string ItemNumber { get; set; }
public string Description { get; set; }
}
Then, iterate through your string, returning a record when you've got a description:
public static IEnumerable<Record> ReadFile(string input)
{
// Alter these as appropriate
const int RECORDTYPELENGTH = 2;
const int COMPANYNAMELENGTH = 41;
const int ITEMNUMBERLENGTH = 8;
const int DESCRIPTIONLENGTH = 48;
int index = 0;
string companyName = null;
string itemNumber = null;
while (index < input.Length)
{
string recordType = input.Substring(index, RECORDTYPELENGTH);
index += RECORDTYPELENGTH;
if (recordType == "00")
{
companyName = input.Substring(index, COMPANYNAMELENGTH).Trim();
index += COMPANYNAMELENGTH;
}
else if (recordType == "01")
{
itemNumber = input.Substring(index, ITEMNUMBERLENGTH).Trim();
index += ITEMNUMBERLENGTH;
}
else if (recordType == "02")
{
string description = input.Substring(index, DESCRIPTIONLENGTH).Trim();
index += DESCRIPTIONLENGTH;
yield return new Record
{
CompanyName = companyName,
ItemNumber = itemNumber,
Description = description
};
}
else
{
throw new FormatException("Unexpected record type " + recordType);
}
}
}
Note that your field lengths in the question don't match the sample data, so I adjusted them so that the solution worked with the data you provided. You can adjust the field lengths by adjusting the constants.
Use this like the following:
string input = "00CompanyName 0115522 02Description 0116622 02Description ";
foreach (var record in ReadFile(input))
{
Console.WriteLine("{0}\t{1}\t{2}", record.CompanyName, record.ItemNumber, record.Description);
}
If you read the whole file into a string, you have a couple options.
One, it might be useful to use string.split.
Another option would be to use string.indexof. Once you have the index, you could use string.substring
Assuming fixed-width as specified, lets create two simple classes to hold a client and its related data as a list:
// can hold as many items (data) as there are in the line
public class Client
{
public string name;
public List<ClientData> data;
};
// one single item in the client data
public class ClientData
{
public string code;
public string description;
};
To parse a single line (which is assumed to have a single client and a successive list of item/description), we can do this (note: for simplification I'm just creating a static class with a static method in it):
// this parser will read as many itens as there are in the line
// and return a Client instance with those inside.
public static class Parser
{
public static Client ParseData(string line)
{
Client client = new Client ();
client.data = new List<ClientData> ();
client.name = line.Substring (2, 10);
// remove the client name
line = line.Substring (12);
while (line.Length > 0)
{
// create new item
ClientData data = new ClientData ();
data.code = line.Substring (2, 10);
data.description = line.Substring (14, 50);
client.data.Add (data);
// next item
line = line.Substring (64);
}
return client;
}
}
So, in your main loop, just after reading a new line from the file, you can call the above method to receive a new client. Something like this:
// should be from a file but this is just an example
string[] lines = {
"00XXXXXXXXXX01YYYYYYYYYY02XXXXXXXXX.XXXXXXXXX.XXXXXXXXX.XXXXXXXXX.XXXXXXXXXX",
"00XXXXXXXXXX01YYYYYYYYYY02XXXXXXXXX.XXXXXXXXX.XXXXXXXXX.XXXXXXXXX.XXXXXXXXXX01YYYYYYYYYY02XXXXXXXXX.XXXXXXXXX.XXXXXXXXX.XXXXXXXXX.XXXXXXXXXX",
"00XXXXXXXXXX01YYYYYYYYYY02XXXXXXXXX.XXXXXXXXX.XXXXXXXXX.XXXXXXXXX.XXXXXXXXXX",
"00XXXXXXXXXX01YYYYYYYYYY02XXXXXXXXX.XXXXXXXXX.XXXXXXXXX.XXXXXXXXX.XXXXXXXXXX",
"00XXXXXXXXXX01YYYYYYYYYY02XXXXXXXXX.XXXXXXXXX.XXXXXXXXX.XXXXXXXXX.XXXXXXXXXX",
};
// loop through each line
// (lines can have multiple items)
foreach (string line in lines)
{
Client client = Parser.ParseData (line);
Console.WriteLine ("Read: " + client.name);
}
Contents of Sample.txt:
00Company1 0115522 02This is a description for company 1. 00Company2 0115523 02This is a description for company 2. 00Company3 0115524 02This is a description for company 3
Note that in the code below, the fields are 2 characters longer than those specified in the original question. This is because I am including the headings in the length of each field, thus a field of a length of 10is effectively 12 by including the 00 from the heading. If this is undesirable, tweak the offsets of the entries in the fieldLengths array.
String directory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
String file = "Sample.txt";
String path = Path.Combine(directory, file);
Int32[] fieldLengths = new Int32[] { 12, 12, 52 };
List<RowData> rows = new List<RowData>();
Byte[] buffer = new Byte[fieldLengths.Sum()];
using (var stream = File.OpenRead(path))
{
while (stream.Read(buffer, 0, buffer.Length) > 0)
{
List<String> fieldValues = new List<String>();
Int32 offset = 0;
for (int i = 0; i < fieldLengths.Length; i++)
{
var value = Encoding.UTF8.GetString(buffer, offset, fieldLengths[i]);
fieldValues.Add(value);
offset += fieldLengths[i];
}
String companyName = fieldValues[0];
String itemNumber = fieldValues[1];
String description = fieldValues[2];
var row = new RowData(companyName, itemNumber, description);
rows.Add(row);
}
}
Class definition for RowData:
public class RowData
{
public String Company { get; set; }
public String Number { get; set; }
public String Description { get; set; }
public RowData(String company, String number, String description)
{
Company = company;
Number = number;
Description = description;
}
}
The results will be in the rows variable.
You would have to split rows based on a delimiter. It would seem that in your case you are using whitespace as a delimiter.
The method you are looking for is String.Split(), it should cover your needs :) Documentation is located at https://msdn.microsoft.com/en-us/library/system.string.split(v=vs.110).aspx - It also includes examples.
I'd do something like this:
string myLineOfText = "MyCompany 12345 The description of my company";
string[] partsOfMyLine = myLineOfText.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
Best of luck! :)

formatting string builder in a For loop

I have a function for my webform which outputs each item within my cart using a for loop. However, my problem is to format this string so it includes tabs spaces, and a new line for each item in the cart. My code stands as this:
public string Display()
{
CartClass CartList = CartClass.GetCart();
System.Text.StringBuilder sb = new StringBuilder();
for (int i = 0; i < CartList.CartList.Count(); i++)
{
Movies Movie = CartList.CartList[i];
sb.Append(String.Format(i + 1 + "." + "\t"
+ Movie.MovieName + "\t" + "£" + Movie.MovieCost.ToString() + "\n" ));
}
return sb.ToString();
}
also to note that I output this string within a asp:listbox using this function
private void DisplayCart()
{
lstCart.Items.Clear();
lstCart.Items.Add(cart.Display());
}
and my out come is this
I'd like the format to resemble a list. For example
Up £5
Madagascar £5
Finding Nemo £5
how can I solve this?
It is not clear what you want in your line to be displayed, but assuming that you want a progressive number, the MovieName and its cost formatted with the current currency symbol, you could use directly the AppendFormat method and the rules of composite formatting
for (int i = 0; i < CartList.CartList.Count(); i++)
{
Movies Movie = CartList.CartList[i];
sb.AppendFormat("{0}.\t{1}\t{2:C}\n", i+1, Movie.MovieName, Movie.MovieCost);
}
The key point here is that both string.Format and StringBuilder.AppendFormat require a format string with placeholders embedded in curly braces ({0}....{1}) where the arguments following the format specifier will be inserted.
See Composite Formatting
However, your problem is caused by adding the whole stringbuilder as one single item. The newline character doesn't break you string in two, it is simply ignored by the listbox items collection.
You need to add one item at time. ( or look at the answer of Mr. Carey)
private void DisplayCart()
{
lstCart.Items.Clear();
CartClass CartList = CartClass.GetCart();
for (int i = 0; i < CartList.CartList.Count(); i++)
{
Movies Movie = CartList.CartList[i];
lstCart.Items.Add(string.Format("{0}.\t{1}\t{2:C}",
i+1, Movie.MovieName, Movie.MovieCost);
}
}
Assuming you want to segregate each movie from the cart list and add each movie as an item to the ListBox, you can use yield return from within the for loop to output an enumerable instead of a composite string and then you can enumerate the list to add each item to the ListBox.
You can do something like:
public IEnumerable<string> Display()
{
CartClass CartList = CartClass.GetCart();
for (int i = 0; i < CartList.CartList.Count(); i++)
{
Movies Movie = CartList.CartList[i];
yield return String.Format(i + 1 + "." + "\t"
+ Movie.MovieName + "\t" + "£" + Movie.MovieCost.ToString() + "\n" );
}
}
And then in the DisplayCart function:
private void DisplayCart()
{
lstCart.Items.Clear();
foreach (var movie in cart.Display())
{
lstCart.Items.Add(movie);
}
}
Assuming you'r using an ASP.Net ListBox control...
Well, any sequence of 1 or more whitespace characters save is, per the HTML spec, collapsed to a single SP (0x20) character when displayed on an HTML page. So there' not much point in differentiating between HT (tab) and SP.
What you probably want to do is to use data binding to populate your listbox. In which case your code becomes something like this:
CartClass cart = CartClass.GetCart() ;
IEnumerable<Movie> movies = cart.CartList ;
lstCart.DataSource = movies;
lstCart.DataTextField = <property-to-be-displayed-to-user>
lstCart.DataValueField = <property-to-be-used-when-item-is-selected>
lstCart.DataBind();
If you really want to format a string, you might look at doing something like this:
public string Display()
{
CartClass cart = CartClass.GetCart();
StringBuilder sb = new StringBuilder();
for ( int i = 0 ; i < cart.CartList.Count() ; i++ )
{
Movie movie = cart.CartList[i];
sb.AppendFormat("{0}.\t{1}\t{2:C}" , i+1 , movie.MovieName , movie.MovieCost ).AppendLine() ;
}
return sb.ToString();
}
The C format specifier is "currency". Assuming you're in the UK, your default CultureInfo should format the movie cost as a proper UK price/currency value.

Get first value in column from CSV file in C#

I am using this code in my Web Api to get data from a csv file, and plug that data into a Item List.
private List<Item> ietms = new List<Item>();
public ItemRepository()
{
string filename = HttpRuntime.AppDomainAppPath + "App_Data\\items.csv";
var lines = File.ReadAllLines(filename).Skip(1).ToList();
for (int i = 0; i < lines.Count; i++)
{
var line = lines[i];
var columns = line.Split('$');
//get rid of newline characters in the middle of data lines
while (columns.Length < 9)
{
i += 1;
line = line.Replace("\n", " ") + lines[i];
columns = line.Split('$');
}
//Remove Starting and Trailing open quotes from fields
columns = columns.Select(c => { if (string.IsNullOrEmpty(c) == false) { return c.Substring(1, c.Length - 2); } return string.Empty; }).ToArray();
items.Add(new Item()
{
Id = int.Parse(columns[0]),
Name = columns[1],
Description = columns[2],
Price = string.IsNullOrEmpty(columns[3].Trim()) ? null : (double?)double.Parse(columns[3]),
Weight = columns[8],
PhotoUrl = columns[7],
Category=columns[9]
});
}
}
In the csv file one of the columns/value is structured like this:
Groups>Subgroup>item
or in some cases
MajorGroup|Groups>Subgroup>item
How do I pull out only the first value before the > or |, so that I would get the value as Groups in the first case and MajorGroup in the second, and store it in the Category property in the Item List, which is now just set to the entire value in column 9 which would return the whole string "Groups>Subgroup>item".
Add the following line before calling "Add item"
var temp = columns[9].Split('|', '>');
Then assign the category as follows.
Category = temp[0];
Based on: MSDN String Method Documentation
Did you mean something like this?
string data = "MajorGroup|Groups>Subgroup>item";
string groupOrCategory;
if (data.Contains('|'))
{
groupOrCategory = data.Substring(0, data.IndexOf('|'));
}
else
{
groupOrCategory = data.Substring(0, data.IndexOf('>'));
}
Console.WriteLine(groupOrCategory);

Categories