Read from textfile and split each line into different strings - c#

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

Related

How to retrieve only few columns data of a csv using the column names instead of column number in c#

I have a csv consisting of many columns. From that csv I have to select only few required columns.
The code I have written is
for (int i = 0; i < lineCount; i++)
{
var line = str.ReadLine();
if (line != null)
{
var values = line.Split(',');
dataInformation.Add(new DataInformation
{
timestamp_iso = values[3],
last_attributed_touch_data_tilde_campaign = values[9],
last_attributed_touch_data_tilde_channel = values[11],
last_attributed_touch_data_tilde_feature = values[12],
last_attributed_touch_data_tilde_ad_set_name = values[19],
user_data_platform = values[69],
user_data_aaid = values[70],
user_data_idfa = values[71],
user_data_idfv = values[72]
});
}
}
I am getting wrong values while using this. Is there any other approach to retrieve the values using the column names instead of column numbers.
The Data Information is a class
public class DataInformation
{
public string timestamp_iso { get; set; }
public string last_attributed_touch_data_tilde_campaign { get; set; }
public string last_attributed_touch_data_tilde_channel { get; set; }
public string last_attributed_touch_data_tilde_feature { get; set; }
public string last_attributed_touch_data_tilde_ad_set_name { get; set; }
public string user_data_platform { get; set; }
public string user_data_aaid { get; set; }
public string user_data_idfa { get; set; }
public string user_data_idfv { get; set; }
}
Please help me on this.
I recommend using a library to deal with CSV format. CsvHelper is a good one. It allows accessing fields by column name:
csv.Read();
var field = csv["HeaderName"];
CSV format may look simple, but there are a few corner cases (like quotes), so it is better to use an existing solution.
I have used the below code to get all the records of the type DataInformation.
using (TextReader fileReader = File.OpenText(FileName))
{
var csv = new CsvReader(fileReader);
dataInformation = csv.GetRecords<DataInformation>().ToList();
}
And after that I have used the below code to get the required columns.
using (TextWriter writer = new StreamWriter(ConfigurationManager.AppSettings["downloadFilePath"] + ConfigurationManager.AppSettings["fileName"] + date + ConfigurationManager.AppSettings["csvExtension"].ToString()))
{
using (var csv = new CsvWriter(TextWriter.Synchronized(writer)))
{
csv.WriteHeader(typeof(DataInformation));
csv.NextRecord();
csv.WriteRecords(dataInformation);
}
}
It works for me.

Converting objects with string[] properties to CSV or Excel

Is there any library out there that can serialize objects with array properties to .csv?
Let's say I have this model:
public class Product
{
public string ProductName { get; set; }
public int InStock { get; set; }
public double Price { get; set; }
...
public string[] AvailableVariants { get; set; }
}
Would something like that be possible to do?
Edit: I need to present some data in a csv/excel format. The thing is, I'm not sure if there is a simple way of achieving what I want with CSV serialization libraries or if I should rather focus on writing an Excel native file.
An example of result I'm looking for:
Product Name In Stock Price Variants
ABC 241 200 Normal
CAB 300 300 Normal
Red
Blue
CBA 125 100 Normal
White
Awesome
Red
ACB 606 75 Normal
Small
Large
X-Large
What would be the most efficient way to do this?
I'm not aware of any libraries that will do this, here's a console example of how I'd approach writing/reading from a CSV:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace TestingProduct
{
class TestingProduct
{
public class Product
{
public string ProductName { get; set; }
public int InStock { get; set; }
public double Price { get; set; }
public string[] AvailableVariants { get; set; }
public override string ToString() => $"{ProductName},{InStock},{Price}{(AvailableVariants?.Length > 0 ? "," + string.Join(",", AvailableVariants) : "")}";
public static Product Parse(string csvRow)
{
var fields = csvRow.Split(',');
return new Product
{
ProductName = fields[0],
InStock = Convert.ToInt32(fields[1]),
Price= Convert.ToDouble(fields[2]),
AvailableVariants = fields.Skip(3).ToArray()
};
}
}
static void Main()
{
var prod1 = new Product
{
ProductName = "test1",
InStock= 2,
Price = 3,
AvailableVariants = new string[]{ "variant1", "variant2" }
};
var filepath = #"C:\temp\test.csv";
File.WriteAllText(filepath, prod1.ToString());
var parsedRow = File.ReadAllText(filepath);
var parsedProduct = Product.Parse(parsedRow);
Console.WriteLine(parsedProduct);
var noVariants = new Product
{
ProductName = "noVariants",
InStock = 10,
Price = 10
};
var prod3 = new Product
{
ProductName = "test2",
InStock= 5,
Price = 5,
AvailableVariants = new string[] { "variant3", "variant4" }
};
var filepath2 = #"C:\temp\test2.csv";
var productList = new List<Product> { parsedProduct, prod3, noVariants };
File.WriteAllText(filepath2, string.Join("\r\n", productList.Select(x => x.ToString())));
var csvRows = File.ReadAllText(filepath2);
var newProductList = new List<Product>();
foreach (var csvRow in csvRows.Split(new string[] { "\r\n" }, StringSplitOptions.None))
{
newProductList.Add(Product.Parse(csvRow));
}
newProductList.ForEach(Console.WriteLine);
Console.ReadKey();
}
}
}
This code will work with a class that has a single object array property. Do you need something that can handle an object with multiple array properties?
I have written some kind of library to write csv files, have a look:
public static class CsvSerializer
{
public static bool Serialize<T>(string path, IList<T> data, string delimiter = ";")
{
var csvBuilder = new StringBuilder();
var dataType = typeof(T);
var properties = dataType.GetProperties()
.Where(prop => prop.GetCustomAttribute(typeof(CsvSerialize)) == null);
//write header
foreach (var property in properties)
{
csvBuilder.Append(property.Name);
if (property != properties.Last())
{
csvBuilder.Append(delimiter);
}
}
csvBuilder.Append("\n");
//data
foreach (var dataElement in data)
{
foreach (var property in properties)
{
csvBuilder.Append(property.GetValue(dataElement));
if (property != properties.Last())
{
csvBuilder.Append(delimiter);
}
}
csvBuilder.Append("\n");
}
File.WriteAllText(path, csvBuilder.ToString());
return true;
}
}
public class CsvSerialize : Attribute
{
}
Lets pretend you want to serialize following class:
public class MyDataClass
{
[CsvSerialize]
public string Item1 {get; set;}
[CsvSerialize]
public string Item2 {get; set;}
}
Then just do:
public void SerializeData(IList<MyDataClass> data)
{
CsvSerializer.Serialize("C:\\test.csv", data);
}
It takes a IList of your class and writes a csv.
It cant serialize arrays but that would be easy to implement.

Parsing txt file to List

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

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

compare properties in classes of list in class

What I've got are two classes which each contain Lists of Classes with propperties of different types. The first list is an updated version of the second and i need to find all differences (deleted/added classes in lists and updated classes).
public class ClassOfKb
{
public List<Data> KbData {get;set;}
public List<Info> KbInfo {get;set;}
}
class Data
{
public Guid ID {get;set}
public byte[] file {get;set}
public string name {get;set}
}
class Info
{
public Guid ID {get;set}
public string text {get;set}
public DateTime date {get;set}
}
ClassOfKb KbA = new ClassOfKb();
ClassOfKb KbB = new ClassOfKb();
first KbA and KbB will be filled from the same DataSet, then i delete, add and modify some of KbA Child-Classes.
now i need to compare KbA with KbB to find out where the differences are. i need the ID of deleted or added classes in KbA and the exact changes of modified Child-Classes properties. How would i do this? Preffered with Linq.
I suggest that create two comparers one for Data and one for Info
class DataComparer : IEqualityComparer<Data>
{
public bool Equals(Data x, Data y)
{
//logic to compare x to y and return true when they are equal
}
public int GetHashCode(Data d)
{
//logic to return a hash code
}
}
class InfoComparer : IEqualityComparer<Info>
{
public bool Equals(Info x, Info y)
{
//logic to compare x to y and return true when they are equal
}
public int GetHashCode(Info i)
{
//logic to return a hash code
}
}
The you can use Intersect and Except LINQ methods
IEnumerable<Data> DataInAandNotInB = KbA.KbData.Except(KbB.KbData,new DataComparer());
IEnumerable<Info> InfoInAandInB = KbA.KbInfo.Intersect(KbB.KbInfo,new InfoComparer ());
For simplicity, I skipped comparison of the byte array and DateTime data membes, only left the IDs and the string data members, but to add them you will need some small modification.
The test is very-very basic, but shows all three of the changes options:
static void Main(string[] args)
{
ClassOfKb KbA = new ClassOfKb();
ClassOfKb KbB = new ClassOfKb();
// Test data --------
Data data1 = new Data() { ID = Guid.NewGuid(), name = "111" };
Data data2 = new Data() { ID = Guid.NewGuid(), name = "222" };
Data data2_changed = new Data() { ID = data2.ID, name = "222_changed" };
Data data3 = new Data() { ID = Guid.NewGuid(), name = "333" };
Info info1 = new Info() { ID = Guid.NewGuid(), text = "aaa" };
Info info2 = new Info() { ID = Guid.NewGuid(), text = "bbb" };
Info info2_changed = new Info() { ID = info2.ID, text = "bbb_changed" };
Info info3 = new Info() { ID = Guid.NewGuid(), text = "ccc" };
KbA.KbData.Add(data1);
KbA.KbData.Add(data2);
KbA.KbInfo.Add(info1);
KbA.KbInfo.Add(info2);
KbB.KbData.Add(data2_changed);
KbB.KbData.Add(data3);
KbB.KbInfo.Add(info2_changed);
KbB.KbInfo.Add(info3);
// end of test data ---------
// here is the solution:
var indexes = Enumerable.Range(0, KbA.KbData.Count);
var deleted = from i in indexes
where !KbB.KbData.Select((n) => n.ID).Contains(KbA.KbData[i].ID)
select new
{
Name = KbA.KbData[i].name,
KbDataID = KbA.KbData[i].ID,
KbInfoID = KbA.KbInfo[i].ID
};
Console.WriteLine("deleted:");
foreach (var val in deleted)
{
Console.WriteLine(val.Name);
}
var added = from i in indexes
where !KbA.KbData.Select((n) => n.ID).Contains(KbB.KbData[i].ID)
select new
{
Name = KbB.KbData[i].name,
KbDataID = KbB.KbData[i].ID,
KbInfoID = KbB.KbInfo[i].ID
};
Console.WriteLine("added:");
foreach (var val in added)
{
Console.WriteLine(val.Name);
}
var changed = from i in indexes
from j in indexes
where KbB.KbData[i].ID == KbA.KbData[j].ID &&
(//KbB.KbData[i].file != KbA.KbData[j].file ||
KbB.KbData[i].name != KbA.KbData[j].name ||
//KbB.KbInfo[i].date != KbA.KbInfo[j].date ||
KbB.KbInfo[i].text != KbA.KbInfo[j].text
)
select new
{
Name = KbA.KbData[j].name,
KbDataID = KbA.KbData[j].ID,
KbInfoID = KbA.KbInfo[j].ID
};
Console.WriteLine("changed:");
foreach (var val in changed)
{
Console.WriteLine(val.Name);
}
Console.ReadLine();
}
}
public class ClassOfKb
{
public List<Data> KbData = new List<Data>();
public List<Info> KbInfo = new List<Info>();
}
public class Data
{
public Guid ID { get; set; }
public byte[] file { get; set; }
public string name { get; set; }
}
public class Info
{
public Guid ID { get; set; }
public string text { get; set; }
public DateTime date { get; set; }
}

Categories

Resources