Parsing txt file to List - c#

I have a txt file that want to store into a array. I'm running into a problem parsing when I have additional items on single line, as the pipe(|) would indicate another item to be stored.
Inv # Date term qty description price Tax
3221409:2017/01/12:215|10:WD2002:2TB Hard Drive:121.66:N|20:KG240S:240GB SSD:125.10:N|20:KG120S:120GB SSD:78.75:N
I'm trying to first try to open the file and show each element to the console. getting index outside the bounds of the array. The file doesn't have headers I provided them for information.
//this object lets you read from a file.
StreamReader streamReader = null;
string lineData;
string[] lineElements;
if (File.Exists(path))
{
Console.WriteLine("Woohoo file found");
try
{
int invoice;
String invoicedate;
int term;
int qty;
string description;
Boolean tax;
streamReader = new StreamReader(path);
while (streamReader.Peek() > 0)
{
lineData = streamReader.ReadLine();
var parts = lineData.Split('|');
lineElements = parts.First().Split(':');
invoice = int.Parse(lineElements[0]);
invoicedate = (lineElements[1]);
term = int.Parse(lineElements[2]);
qty = int.Parse(lineElements[3]);
Console.WriteLine(invoice);
Console.WriteLine(invoicedate);
Console.WriteLine(term);
Console.WriteLine(qty);
}

First define a structure that can hold the informations
public class InventoryData
{
public string Inv { get; set; }
public DateTime Date { get; set; }
public string Term { get; set; }
public IList<InventoryArticle> Articles { get; set; }
}
public class InventoryArticle
{
public int Quantity { get; set; }
public string Whatever { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public bool Tax { get; set; }
}
then two methods, that will parse a single line into the structure
static InventoryData ParseInventoryData( string data )
{
var parts = data.Split( '|' );
var headparts = parts.First().Split( ':' );
var result = new InventoryData
{
Inv = headparts[0],
Date = DateTime.ParseExact( headparts[1], "yyyy/MM/dd", System.Globalization.CultureInfo.InvariantCulture ),
Term = headparts[2],
Articles = parts.Skip( 1 ).Select( part => ParseInventoryArticle( part ) ).ToList(),
};
return result;
}
static InventoryArticle ParseInventoryArticle( string data )
{
var parts = data.Split( ':' );
var result = new InventoryArticle
{
Quantity = int.Parse( parts[0] ),
Whatever = parts[1],
Description = parts[2],
Price = decimal.Parse( parts[3], System.Globalization.CultureInfo.InvariantCulture ),
Tax = parts[4] == "Y",
};
return result;
}
and finally how to parse the content of a file
string[] content = {
"Inv # Date term qty description price Tax",
"3221409:2017/01/12:215|10:WD2002:2TB Hard Drive:121.66:N|20:KG240S:240GB SSD:125.10:N|20:KG120S:120GB SSD:78.75:N" };
var data = content
// skip the header row
.Skip( 1 )
// parse the content
.Select( row => ParseInventoryData( row ) )
.ToList();

Related

Read from textfile and split each line into different strings

i have to read from a textfile which contains the following formatting
PRODUCTID PRODUCTNAME CUSTOMERID CUSTOMERNAME AMOUNT.
The textfile contains 11 line and for each one of them i must store each eg. productid into one string, and productname into one string.
I have tried like this which only stores the length of each line..
List<string> list = new List<string>();
using (var reader = new StreamReader(#"Budget.txt"))
{
string line;
while ((line = reader.ReadLine()) != null)
{
list.Add(line);
}
}
EDIT: Created a class which hold the data
Create a class that rapresent your file line, like this:
public class Procuct {
public string ProductId {get;set;}
public string ProductName {get;set;}
public string CustomerId {get;set;}
public string CustomerName {get;set;}
public string Amount{get;set;}
}
Then create a list of product to store them:
List<Procuct> list = new List<Procuct>();
using (var reader = new StreamReader(#"Budget.txt"))
{
string line;
while ((line = reader.ReadLine()) != null)
{
var temp = line.Split(" ");
list.Add(new Product{
ProductId = temp[0],
ProductName = temp[1],
CustomerId = temp[2],
CustomerName = temp[3],
Amount = temp[4]
});
}
}
Once are stored you can use LINQ to get the information that you want.
You have to work with TextFieldParser Class it is used to parse delimited text lignes as columns
using System;
using Microsoft.VisualBasic.FileIO;
class Program
{
static void Main()
{
using (TextFieldParser parser = new TextFieldParser("C:\\csv.txt"))
{
parser.Delimiters = new string[] { " " };
while (true)
{
string[] parts = parser.ReadFields();
if (parts == null)
{
break;
}
Console.WriteLine("{0} field(s)", parts.Length);
}
}
}
}
There is a useful examples in this Topic
You can use some code like below to get what you want
static void Main(String[] args) {
var allRecords = new List<Record>();
foreach (var line in File.ReadLines("Budget.txt")) {
allRecords.Add(new Record(line));
}
}
public class Record {
public string ProductId { get; private set; }
public string ProductName { get; private set; }
public string CustomerId { get; private set; }
public string CustomerName { get; private set; }
public decimal Amount { get; private set; }
public Record(string line) {
var items = line.Split();
ProductId = items[0];
ProductName = items[1];
CustomerId = items[2];
CustomerName = items[3];
Amount = Convert.ToDecimal(items[4]);
}
public override String ToString() {
return $"ProductId:{ProductId}, ProductName:{ProductName}, CustomerId:{CustomerId}, CustomerName:{CustomerName}, Amount:{Amount}";
}
}
As your question is not very clear, I assume that your file format is as demonstrated by the example below
10 Enchiladas 27 John Doe 15.00
11 HotDogs 27 John Doe 5.00
12 Burgers 29 Jane Doe 10.00
.
.
.
and so on

Iterate through array and add up elements with a control break logic

I am trying to use this for each loop to iterate through this array, and sum up the elements, 12, 10, 8, 7, and 6, when element 0 is not different (position.account), using control break logic,
This is furthering on a question I had earlier, but I can't seem to figure out how to do this logically.
static void Main(string[] args)
{
String path = #"C:\Users\jhochbau\documents\visual studio 2015\Projects\CsvReader\CsvReader\Position_2016_02_25.0415.csv";
//Adding lines read into a string[];
string[] lines = File.ReadAllLines(path);
foreach(string line in lines)
{
Positions position = new Positions();
string[] parsedLine = line.Split(',');
position.account = parsedLine[0];
position.settleMM = parsedLine[10];
position.open = parsedLine[6];
position.buy = parsedLine[7];
position.sell = parsedLine[8];
position.underlying = parsedLine[12];
//Need to convert these to an int.
//for each iteration through the loop where string[0] is already existing
//I want to have sum = sum + string[10]
}
Console.Read();
}
public class Positions
{
public string account { get; set; }
public string symbol { get; set; }
public string prevClose { get; set; }
public string curPrx { get; set; }
public string settlePX { get; set; }
public string Mult { get; set; }
public string open { get; set; }
public string buy { get; set; }
public string sell { get; set; }
public string netMM { get; set; }
public string settleMM { get; set; }
public string settleDay { get; set; }
public string underlying { get; set; }
}
Further to my comment, you could do something like this:
// store the accounts inside this dictionary
var accounts = new Dictionary<string, Positions>();
foreach(string line in lines)
{
Positions position = new Positions();
string[] parsedLine = line.Split(',');
position.account = parsedLine[0];
...
Positions existingAccount;
// if the account already exists in the dictionary
if (accounts.TryGetValue(position.account, out existingAccount)) {
existingAccount.buy += position.buy;
// do updating logic here
} else {
accounts.add(position.account, position);
// otherwise add it as a new element
}
}
Alternatively, you could go for Linq:
File.ReadLines(path)
.Select( line => new Position(line) )
.GroupBy( position => position.account )
.Select( group => new { Account = group.Key,
Sum = group.Select( position => position.settleMM ).Sum() } );

Entity to LINQ upload CSV file where single rows can have multiple values in columns

I'm currently working on parsing a csv file that was exported by another application. This application exported the data in a strange way. This export is from accoutning and it looks similar to this..
I'm trying to figure out a way to read the csv file, then split up the multiple 'All Accounts' values and 'Amt' Values so that M200 and 300.89 is another entry, M300 and 400.54 are another entry, and M400 and 100.00 are another entry. So after inserting this single row into the database, I should actually have 4 rows like so..
This is how I'm currently reading and inserting into the database.
List<RawData> data = new List<RawData>();
try
{
string text = File.ReadAllText(lblFileName.Text);
string[] lines = text.Split('\n');
int total = 0, reduced = 0;
foreach (string line in lines)
{
RawData temp = new RawData(line);
total++;
if (!(temp.FirstAccount.Length == 0 || temp.FirstAccount == "1ST-ACCT-NO"))
{
reduced++;
data.Add(temp);
}
}
}
catch (IOException ex)
{
Console.WriteLine("Unable to read file. " + ex.ToString());
MessageBox.Show(ex.ToString());
}
try
{
foreach (RawData rData in data)
{
tCarsInTransit cit = new tCarsInTransit
{
FIRST_ACCT_NO = rData.FirstAccount,
ACCOUNT_NO_DV = rData.AccountNoDv,
ACCT_NO = rData.AcctNo,
ACCT_NO_L = rData.AccNoL,
ACCT_NUM_DV = rData.AcctNumDv,
ACCT_PFX = rData.AcctPfx,
ACCT_PFX_PRT = rData.AcctPfxPrt,
ACCT_TYPE_DV = rData.AcctTypeDv,
ADV_NO = rData.AdvNo,
ALL_PRT_FLAG = rData.AllPrtFlag,
AMT = rData.Amt,
AMT_GLE = rData.AmtGle,
BASE_GLE = rData.BaseGle,
CNT_CAT = rData.CntCat,
COLD_PRT_FLAG = rData.ColdPrtFlag,
COST_DV = rData.CostDv,
COST_OVRD_FLAG_DV = rData.CostOvrdFlagDv,
CR_ACCT_DV = rData.CrAcctDv,
CR_ACCT_DV_GLE = rData.CrAcctDvGle,
CROSS_POSTING_FLAG = rData.CrossPostingFlag,
CROSS_POST_CAT = rData.CrossPostCat,
CTRL_NO = rData.CtrlNo,
CTRL_TYPE_DV = rData.CtrlTypeDv,
DESC_REQD_DV = rData.DescReqdDv,
DR_ACCT_DV = rData.DrAcctDv,
GL_DIST_ACCT_DV = rData.GLDistAcctDv,
GL_DIST_DV = rData.GLDistDv,
GRP_NO_DV = rData.GrpNoDv,
ID_PORT_DATE_TIME_FMT_CAT = rData.IdPortDateTimeFmtCat,
INACTIVITY_DV = rData.InactivityDv,
JOIN_COL = rData.JoinCol,
JRNL_DATE = rData.JrnlDate,
JRNL_PFX = rData.JrnlPfx
};
tCIT.tCarsInTransits.Add(cit);
tCIT.SaveChanges();
lblMessage.ForeColor = System.Drawing.Color.Green;
lblMessage.Text = "Finished uploading. ";
}
}
catch (DbEntityValidationException ex)
{
foreach (var eve in ex.EntityValidationErrors)
{
Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
eve.Entry.Entity.GetType().Name, eve.Entry.State);
foreach (var ve in eve.ValidationErrors)
{
Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
ve.PropertyName, ve.ErrorMessage);
}
}
throw;
}
I am not sure how to accomplish this. The above currently inserts the csv file into Sql Server the exact way the csv file was exported. Any ideas would greatly be appreciated! Thanks!
EDIT: Here is the RawData class.
class RawData
{
public string FirstAccount { get; set; }
public string AccountNoDv { get; set; }
public string AcctNo { get; set; }
public string AccNoL { get; set; }
public string AcctNumDv { get; set; }
public string AcctPfx { get; set; }
public string AcctPfxPrt { get; set; }
public string AcctTypeDv { get; set; }
public string AdvNo { get; set; }
public string AllPrtFlag { get; set; }
public string Amt { get; set; }
public string AmtGle { get; set; }
public string BaseGle { get; set; }
public string CntCat { get; set; }
public string ColdPrtFlag { get; set; }
public string CostDv { get; set; }
public string CostOvrdFlagDv { get; set; }
public string CrAcctDv { get; set; }
public string CrAcctDvGle { get; set; }
public string CrossPostingFlag { get; set; }
public string CrossPostCat { get; set; }
public string CtrlNo { get; set; }
public string CtrlTypeDv { get; set; }
public string DescReqdDv { get; set; }
public string DrAcctDv { get; set; }
public string GLDistAcctDv { get; set; }
public string GLDistDv { get; set; }
public string GrpNoDv { get; set; }
public string IdPortDateTimeFmtCat { get; set; }
public string InactivityDv { get; set; }
public string JoinCol { get; set; }
public string JrnlDate { get; set; }
public string JrnlPfx { get; set; }
public RawData(string csvString)
{
string[] citData = csvString.Replace(", ", "").Replace(".,", ".").Split(',');
try
{
FirstAccount = citData[0];
AccountNoDv = citData[1];
AcctNo = citData[2];
AccNoL = citData[3];
AcctNumDv = citData[4];
AcctPfx = citData[5];
AcctPfxPrt = citData[6];
AcctTypeDv = citData[7];
AdvNo = citData[8];
AllPrtFlag = citData[9];
Amt = citData[10];
AmtGle = citData[11];
BaseGle = citData[12];
CntCat = citData[13];
ColdPrtFlag = citData[14];
CostDv = citData[15];
CostOvrdFlagDv = citData[16];
CrAcctDv = citData[17];
CrAcctDvGle = citData[18];
CrossPostingFlag = citData[19];
CrossPostCat = citData[20];
CtrlNo = citData[21];
CtrlTypeDv = citData[22];
DescReqdDv = citData[23];
DrAcctDv = citData[24];
GLDistAcctDv = citData[25];
GLDistDv = citData[26];
GrpNoDv = citData[27];
IdPortDateTimeFmtCat = citData[28];
InactivityDv = citData[29];
JoinCol = citData[30];
JrnlDate = citData[31];
JrnlPfx = citData[32];
}
catch (Exception ex)
{
Console.WriteLine("Something went wrong. " + ex.ToString());
}
}
}
EDIT 2: AllAccounts in the images is acutally 'AccountNoDv' and there are actually many different fields that have multiples like 'AccountNoDv'(AllAccounts) but we might be removing those as this is not a final export. As of right now the two fields I'm worried most about are AccountNoDv and Amt.
Try something like this:
foreach (string line in lines)
{
RawData temp = new RawData(line);
var AllAccounts = temp.AccountNoDv.split(' ');
var Amts = temp.Amt.split(' ');
if (AllAccounts.Length() == Amts.Length() && Amts.Length() > 1) {
// We have multiple values!
reduced++;
for (int i = 0; i < AllAccounts.Length(); i++) {
RawData temp2 = RawDataCopy(temp); // Copy the RawData object
temp2.AccountNoDv = AllAccounts[i];
temp2.Amt = Amts[i];
total++;
data.Add(temp2);
}
}
else {
total++;
if (!(temp.FirstAccount.Length == 0 || temp.FirstAccount == "1ST-ACCT-NO"))
{
reduced++;
data.Add(temp);
}
}
}
And:
private RawData RawDataCopy(RawData copyfrom) {
// Write a function here that returns an exact copy from the one provided
// You might have to create a parameterless constructor for RawData
RawData RawDataCopy = new RawData();
RawDataCopy.FirstAccount = copyfrom.FirstAccount;
RawDataCopy.AccountNoDv = copyfrom.AccountNoDv;
RawDataCopy.AcctNo = copyfrom.AcctNo;
// . . . . . . . .
RawDataCopy.JrnlPfx = copyfrom.JrnlPfx;
return RawDataCopy;
}
Then also add a parameterless constructor to your RawData class:
public RawData()
{
}
Perhaps it would be sexier to implement the ICloneable interface and call the Clone() function instead of the RawDataCopy function, but it gets the idea across.
In Linq you can use SelectMany to increase the number of elements in a list. Here is a rough example of how this could be done. I make the assumption that the number of sub elements in AllAccounts and Amt is the same. A more robust solution would check for these issues.
So after you have loaded your data list:
var expandedData =
data.SelectMany(item =>
// split amount (will just return one item if no spaces)
item.Amt.Split(" ".ToCharArray())
// join this to the split of all accounts
.Zip(item.AllAccounts.Split(" ".ToCharArray()),
// return the joined item as a new anon object
(a,b) => new { amt=a, all=b }),
// take the original list item and the anon object and return our new item
(full,pair) => { full.Amt = pair.amt; full.AllAccounts = pair.all; return full; }));
You will now have a list of your data items with the multiple items expanded into the list.
I don't have test data to test so I might have some minor typos -- but I put in lots of comments to make the Linq as clear as possible.
Here is is simple example I wrote in LinqPad for myself to make sure I understood how SelectMany worked:
string [] list = { "a b c d","e","f g" };
var result = list.SelectMany((e) =>
e.Split(" ".ToCharArray()),
(o,item) => new { org = o, item = item}).Dump();

Using delimiters to separate string into variables

I have a list of string where each list item contains 6 values. Each value is separated by a comma. I want to use Split(',') to separate each value then assign each one to a variable
Do I have to use an array to create new variables, or is there a way to separate each value into existing variables?
Currently the code I have to do this is nothing but
foreach (String Item in ItemList)
{
string id="";
string slip = "";
string qty = "";
string itemEntered = "";
string UOM = "";
string desc = "";
string[] vars = Item.Split(',');
}
You just need to extract the split values by index:
string[] vars = line.Split(',');
string id = vars[0];
string slip = vars[1];
string Qty = vars[2];
string Item = vars[3];
string UOM = vars[4];
string Desc = vars[5];
Consider to use an available CSV-Parser instead of doing this manually. However, if your data never contains the separator and it's strict, your approach is fine. You just have to take the strings at the correct indices:
List<Data> data = new List<Data>();
foreach (String line in ItemList)
{
string[] vars = line.Split(',');
if (vars.Length == 6)
{
int id;
if(!int.TryParse(vars[0], out id))
continue;
string slip = vars[1];
int qty;
if(!int.TryParse(vars[2], out qty))
continue;
string item = vars[3];
string uom = vars[4];
string desc = vars[5];
data.Add(new Data { ID = id, Slip = vars[1], Quantity = qty, Desc = desc, Item = item, UOM = uom });
}
}
I have also used a custom class:
public class Data
{
public int ID { get; set; }
public string Slip { get; set; }
public int Quantity { get; set; }
public string Item { get; set; }
public string UOM { get; set; }
public string Desc { get; set; }
}

taking data from a csv file, and putting it into an array

public class Earthquake
{
public double Magnitude { get; set; }
public string Location { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public double depth { get; set; }
public DateTime date { get; set; }
public string EventID { get; set; }
public string URL { get; set; }
public Earthquake()
: this(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty)
{ }
public Earthquake(string magna, string locate, string lat, string longi, string dept, string dat, string Event, string website)
{
Magnitude = Convert.ToDouble(magna);
Location = locate;
Latitude = Convert.ToDouble(lat);
Longitude = Convert.ToDouble(longi);
depth = Convert.ToDouble(dept);
date = Convert.ToDateTime(dat);
EventID = Event;
URL = website;
}
}
public void GetData()
{
string[] text = File.ReadAllLines(#"Earthquakes.csv");
Earthquake[] data = new Earthquake[1];
foreach (string line in text)
{
string[] myColumns = line.Split(',');
Earthquake[] earth = new Earthquake[myColumns[0], myColumns[1], myColumns[2], myColumns[3], myColumns[4], myColumns[5], myColumns[6], myColumns[7]];
data[i] = earth[i];
i++;
}
}
Ignore commented parts I have those under control. The problem I am having is getting the data from the csv file into the Earthquake Array. I am getting syntax errors, and I know why, it's because the data type isn't correct, but I honestly cannot figure out how to fix it.
Also if you notice I am trying to use bubble sort and since there is no definition for "compare" for double, what do I use instead?
If your reading from CSV file you probably have to remove white space from the split values.
Try adding .Trim() to your column variables
myColumns[0].Trim()
if your looking to sort yor array consider using System.Linq
eg:
var byMag = earthQuakes.OrderBy(e => e.Magnitude);
Looking at your code you posted, GetData() will not work.
Try returning a list or Enumerable
public IEnumerable<Earthquake> GetData(string filename)
{
string[] text = File.ReadAllLines(filename);
foreach (string line in text)
{
string[] myColumns = line.Split(',');
yield return new Earthquake(myColumns[0].Trim(), myColumns[1].Trim(), myColumns[2].Trim(), myColumns[3].Trim(), myColumns[4].Trim(), myColumns[5].Trim(), myColumns[6].Trim(), myColumns[7].Trim());
}
}
Usage:
var earthquakes = GetData(#"Earthquakes.csv");

Categories

Resources