I read text file and displayed in DataGridView data is displaying, but in the above text file is related to log files, I want to display that log file by month wise, without using a DataTable, if possible.
private void BtnUser_Click(object sender, EventArgs e)
{
dgv1.Columns.Add("col1", "Ipaddress");
dgv1.Columns.Add("col2", "Sysname");
dgv1.Columns.Add("col3", "username");
dgv1.Columns.Add("col4", "text");
dgv1.Columns.Add("col5", "datetime");
string line;
StreamReader strRead = new StreamReader("D:\\login.lgl");
{
int row = 0;
while ((line = strRead.ReadLine()) != null)
{
string[] columns = line.Split('|');
dgv1.Rows.Add();
for (int i = 0; i < columns.Length; i++)
{
dgv1[i, row].Value = columns[i];
}
row++;
}
}
}
You could use Linq to group by month:
var logMonthGroups = File.ReadLines("D:\\login.lgl")
.Select(l => new { Cols = l.Split('|') })
.Select(x => new
{
Ipaddress = x.Cols.ElementAtOrDefault(0),
Sysname = x.Cols.ElementAtOrDefault(1),
username = x.Cols.ElementAtOrDefault(2),
text = x.Cols.ElementAtOrDefault(3),
datetime = x.Cols.ElementAtOrDefault(4) == null ? DateTime.MinValue : DateTime.Parse(x.Cols[4])
})
.GroupBy(x => new { Year = x.datetime.Year, Month = x.datetime.Month })
.OrderBy(g => g.Key.Year).ThenBy(g => g.Key.Month);
foreach(var group in logMonthGroups)
{
// add to the DataGridView ...
}
I would recommend that you create a class for the structure you are parsing in from the file, something like:
public class LogFileItem
{
public string IpAddress {get; set;}
public string Sysname {get; set;}
public string Username {get; set;}
public string Text {get; set;}
public DateTime DateTime {get; set;}
public static List<LogFileItem> ParseLogFile(string path)
{
List<LogFileItem> result = new List<LogFileItem>();
//in a real scenario, this code should have a lot more error checks
string line;
StreamReader strRead = new StreamReader(path);
while ((line = strRead.ReadLine()) != null)
{
string[] columns = line.Split('|');
LogFileItem item = new LogFileItem();
item.IpAddress = columns[0];
item.Sysname = columns[1];
item.Username = columns[2];
item.Text = columns[3];
//this assumes that the dateTime column is parsable by .net
item.DateTime = DateTime.Parse(columns[4]);
result.add(item);
}
return result;
}
}
then you could just do:
private void BtnUser_Click(object sender, EventArgs e)
{
List<LogFileItem> logItems = LogFileItem.ParseLogFile(#"D:\login.lgl");
dgv1.DataSource = logItems;
}
to display the data. Also you could filter the data any which way you want, and if you have a month/year pair to filter on, you could just do:
List<LogFileItem> logItems = LogFileItem.ParseLogFile(#"D:\login.lgl");
var logsPerMonth = logItems.Where(li => li.DateTime.Year = year && li.DateTime.Month == month);
Note that datetime parsing is somewhat of a dark art, so you could take a look at DateTime.ParseExact to make that work. Also, take a look at using an using statement, or reading the lines from a text file with File.ReadAllLines.
Related
I need to compare two lists of same type. Assume I have CurrentSC List(Current modified data by user) and PreviousSC List(Saved data from database) of below class.
public class SoftClose
{
private int AID = -1;
private bool _softCloseInd;
private bool _softCloseEditInd;
private string _softClosedBy;
private DateTime _softClosedDate;
private ReferenceEnums.ActionStatus _status = ReferenceEnums.ActionStatus.NO_CHANGE;
}
public static void TPostProcessAddRemoveSoftCloseStopPaymentPrefixes(IFPMServiceInternal fpmService, AgreementRevision revision)
{
List<SoftClose> psc = null;
List<SoftClose> csc = null;
string fanValue = revision.Agreement.FAN;
psc = fpmService.GetSoftCloseByFAN(fanValue);
if (psc != null)
{
//var currentprefixes = revision.Details.Where(x => x.Prefix != null).Select(y => y.Prefix).Distinct();
//Create current SoftClose object using revision object
foreach (var prefix in revision.Details.Where(x => x.Prefix != null).Select(y => y.Prefix).Distinct())
{
var newSF =
new SoftClose
{
Id = -1,
Status = ReferenceEnums.ActionStatus.NO_CHANGE,
AgreementRevId = revision.Id,
AgreementId = revision.Agreement.Id,
WorkflowStatus = revision.WorkflowStatus,
FAN = revision.Agreement.FAN,
PID = (int)revision.Agreement.PID,
Prefix = prefix
};
csc.Add(newSF);
}
//Now you have previous and current softcloses to compare prefixes...
psc.OrderBy(x => x.Prefix.Id);
csc.OrderBy(x => x.Prefix.Id);
for(int i = 0; i < csc.Count; i++)
{
}
}
}
Lets say I have changed D3 value in PreviousSC to D2 in CurrentSC. Now i need to delete D3 value from database(As D2 value already there in database i don't need to insert) and chnage _status to DELETE and I added D4 value in CurrentSC which is not there is PreviousSC. Now I need to add D4 value in database and assign _softCloseInd and _softCloseEditInd to Y and change _status to ADD.
How to achieve this in best way?
class Program
{
static void Main(string[] args)
{
List<SoftClose> psc = new List<SoftClose>(){
new SoftClose(){ID=1, Status = "NO_CHANGE",AID=19, Prefix = "D1"},
new SoftClose(){ID=2, Status = "NO_CHANGE",AID=20, Prefix = "D2"},
new SoftClose(){ID=3, Status = "NO_CHANGE",AID=21, Prefix = "D3"},
new SoftClose(){ID=3, Status = "NO_CHANGE",AID=22, Prefix = "D9"}
};
List<SoftClose> csc = new List<SoftClose>(){
new SoftClose(){ID=-1, Status = "NO_CHANGE",AID=19, Prefix = "D2"},
new SoftClose(){ID=-1, Status = "NO_CHANGE",AID=20, Prefix = "D2"},
new SoftClose(){ID=-1, Status = "NO_CHANGE",AID=21, Prefix = "D6"},
new SoftClose(){ID=-1, Status = "NO_CHANGE",AID=22, Prefix = "D4"},
new SoftClose(){ID=-1, Status = "NO_CHANGE",AID=23, Prefix = "D5"},
new SoftClose(){ID=-1, Status = "NO_CHANGE",AID=24, Prefix = "D3"}
};
List<SoftClose> esc = new List<SoftClose>();
Console.WriteLine("---------Previous List----------");
foreach (var item in psc)
{
Console.WriteLine($"Id:{item.ID}, Desc1:{item.Prefix}, Status:{item.Status}");
}
Console.WriteLine("--------------------------------------");
Console.WriteLine("---------Current List----------");
foreach (var item in csc)
{
Console.WriteLine($"Id:{item.ID}, Desc1:{item.Prefix}, Status:{item.Status}");
}
Console.WriteLine("--------------------------------------");
var addlist = csc.Where(c => psc.All(p => !p.Prefix.Equals(c.Prefix)));
foreach (var n in addlist)
{
var index = csc.FindIndex(p => p.Prefix.Equals(n.Prefix));
csc[index].Status = "ADD";
esc.Add(csc[index]);
}
var deletelist = psc.Where(p => p.Status.Equals("NO_CHANGE") && !csc.Exists(c => c.Prefix.Equals(p.Prefix)));
foreach (var n in deletelist)
{
var index = psc.FindIndex(c => c.Prefix.Equals(n.Prefix));
if (psc.FindIndex(c => c.Prefix.Equals(n.Prefix)) >= 0)
{
psc[index].Status = "REMOVE";
esc.Add(psc[index]);
}
}
Console.WriteLine("---------Effective List----------");
foreach (var item in esc)
{
Console.WriteLine($"Id:{item.ID}, Prefix:{item.Prefix}, Status:{item.Status}");
}
Console.ReadLine();
}
}
public class SoftClose
{
public int ID = -1;
public int AID = -1;
public int WFID = -1;
public string Prefix;
public DateTime SCDATE;
public string Status;
}
I have a large csv file which has millions of rows. The sample csv lines are
CODE,COMPANY NAME, DATE, ACTION
A,My Name , LLC,2018-01-28,BUY
B,Your Name , LLC,2018-01-25,SELL
C,
All Name , LLC,2018-01-21,SELL
D,World Name , LLC,2018-01-20,BUY
Row C has new line, but actually this is same record. I want to remove new line character from the csv line within cell\field\column.
I tired \r\n, Envirnment.NewLine and many other things, but could not make it work.
Here is my code..
private DataTable CSToDataTable(string csvfile)
{
Int64 row = 0;
try
{
string CSVFilePathName = csvfile; //#"C:\test.csv";
string[] Lines = File.ReadAllLines(CSVFilePathName.Replace(Environment.NewLine, ""));
string[] Fields;
Fields = Lines[0].Split(new char[] { ',' });
int Cols = Fields.GetLength(0);
DataTable dt = new DataTable();
//1st row must be column names; force lower case to ensure matching later on.
for (int i = 0; i < Cols; i++)
dt.Columns.Add(Fields[i].ToLower(), typeof(string));
DataRow Row;
for (row = 1; row < Lines.GetLength(0); row++)
{
Fields = Lines[row].Split(new char[] { ',' });
Row = dt.NewRow();
//Console.WriteLine(row);
for (int f = 0; f < Cols; f++)
{
Row[f] = Fields[f];
}
dt.Rows.Add(Row);
if (row == 190063)
{
}
}
return dt;
}
catch (Exception ex)
{
throw ex;
}
}
How can I remove new line character and read the row correctly? I don't want to skip the such rows as per the business requirement.
You CSV file is not in valid format. In order to parse and load them successfully, you will have to sanitize them. Couple of issues
COMPANY NAME column contains field separator in it. Fix them by
surrounding quotes.
New line in CSV value - This can be fixed by combining adjacent rows as one.
With Cinchoo ETL, you can sanitize and load your large file as below
string csv = #"CODE,COMPANY NAME, DATE, ACTION
A,My Name , LLC,2018-01-28,BUY
B,Your Name , LLC,2018-01-25,SELL
C,
All Name , LLC,2018-01-21,SELL
D,World Name , LLC,2018-01-20,BUY";
string bufferLine = null;
var reader = ChoCSVReader.LoadText(csv)
.WithFirstLineHeader()
.Setup(s => s.BeforeRecordLoad += (o, e) =>
{
string line = (string)e.Source;
string[] tokens = line.Split(",");
if (tokens.Length == 5)
{
//Fix the second and third value with quotes
e.Source = #"{0},""{1},{2}"",{3}, {4}".FormatString(tokens[0], tokens[1], tokens[2], tokens[3], tokens[4]);
}
else
{
//Fix the breaking lines, assume that some csv lines broken into max 2 lines
if (bufferLine == null)
{
bufferLine = line;
e.Skip = true;
}
else
{
line = bufferLine + line;
tokens = line.Split(",");
e.Source = #"{0},""{1},{2}"",{3}, {4}".FormatString(tokens[0], tokens[1], tokens[2], tokens[3], tokens[4]);
line = null;
}
}
});
foreach (var rec in reader)
Console.WriteLine(rec.Dump());
//Careful to load millions rows into DataTable
//var dt = reader.AsDataTable();
Hope it helps.
You haven't made it clear what are the possible criteria an unwanted new line could appear in the file. So assuming that a 'proper' line in the CSV file does NOT end with a comma, and if one ends with a comma that means that it's not a properly formatted line, you could do something like this:
static void Main(string[] args)
{
string path = #"CSVFile.csv";
List<CSVData> data = new List<CSVData>();
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
using (StreamReader sr = new StreamReader(fs))
{
sr.ReadLine(); // Header
while (!sr.EndOfStream)
{
var line = sr.ReadLine();
while (line.EndsWith(","))
{
line += sr.ReadLine();
}
var items = line.Split(new string[] { "," }, StringSplitOptions.None);
data.Add(new CSVData() { CODE = items[0], NAME = items[1], COMPANY = items[2], DATE = items[3], ACTION = items[4] });
}
}
}
Console.ReadLine();
}
public class CSVData
{
public string CODE { get; set; }
public string NAME { get; set; }
public string COMPANY { get; set; }
public string DATE { get; set; }
public string ACTION { get; set; }
}
Obviously there's a lot of error handling to be done here (for example, when creating a new CSVData object make sure your items contain all the data you want), but I think this is the start you need.
private void Form1_Load(object sender, EventArgs e)
{
using (StreamReader sr = new StreamReader("1.csv"))
{
string headerLine = sr.ReadLine();
String line;
while (sr.Peek() != -1) // Just read the first line and do nothing with it...
while ((line = sr.ReadLine()) != null)
{
string[] parts = line.Split(',');
string day = parts[3];
string month = parts[2];
string year = parts[1];
string OldDate = (day + "/" + month + "/" + year);
DateTime dt1 = DateTime.Parse(OldDate);
DateTime dt2 = DateTime.Now;
if (dt1.Date >= dt2.Date)
{
MessageBox.Show(dt1 + " still relevant ");
}
else
{
// How do I delete rows with a date already passed ?
}
}
You can recreate the text files with the lines you want to keep. Either use your StreamReader and fill a List<string> or use this LINQ approach:
List<string> lines = File.ReadLines("1.csv")
.Select(l => new{ Line = l, Parts = l.Split(',') })
.Where(x => x.Parts.Length >= 4)
.Select(x => new {
x.Line,
Day = x.Parts[3].Trim().TryGetInt32(),
Month = x.Parts[2].Trim().TryGetInt32(),
Year = x.Parts[1].Trim().TryGetInt32(),
})
.Where(x => x.Day.HasValue && x.Month.HasValue && x.Year.HasValue)
.Select(x => new {x.Line, Date = new DateTime(x.Year.Value, x.Month.Value, x.Day.Value) })
.Where(x => x.Date >= DateTime.Now)
.Select(x => x.Line)
.ToList();
File.WriteAllLines("1.csv", lines);
Used this extension method which comes in handy in LINQ queries like this:
public static int? TryGetInt32(this string item)
{
int i;
bool success = int.TryParse(item, out i);
return success ? (int?)i : (int?)null;
}
For this you need to keep track of passed date, Here i suggest you to use a List<DateTime> Add to the list if it contains the current fetched date. So that Contains is true means the date is already passed. in short you can use like the following:
List<string> linesInFile = File.ReadLines("yourFile.csv").ToList();
List<DateTime> passedDateList = new List<DateTime>();
List<string> duplicateLines = new List<string>();
foreach (var item in linesInFile)
{
//extract value for date
string OldDate = (day + "/" + month + "/" + year);
DateTime dt1 = DateTime.Parse(OldDate);
if (passedDateList.Contains(dt1))
{
duplicateLines.Add(item);
// the date is already passed
}
else
{
// it is no yet passed
//Do your task here
passedDateList.Add(dt1);
}
}
linesInFile = linesInFile.Except(duplicateLines).ToList(); // remove already passed line
File.WriteAllLines("yourFile.csv", linesInFile); // write back to the file
This is the Input my file contains:
50|Hallogen|Mercury|M:4;C:40;A:1
90|Oxygen|Mars|M:10;C:20;A:00
5|Hydrogen|Saturn|M:33;C:00;A:3
Now i want to split each and every line of my text file and store in my class file like :
Expected output:
Planets[0]:
{
Number:50
name: Hallogen
object:Mercury
proportion[0]:
{
Number:4
},
proportion[1]:
{
Number:40
},
proportion[2]:
{
Number:1
}
}
etc........
My class file to store all this values:
public class Planets
{
public int Number { get; set; } //This field points to first cell of every row.output 50,90,5
public string name { get; set; } //This field points to Second cell of every row.output Hallogen,Oxygen,Hydrogen
public string object { get; set; } ////This field points to third cell of every row.output Mercury,Mars,Saturn
public List<proportion> proportion { get; set; } //This will store all proportions with respect to planet object.
//for Hallogen it will store 4,40,1.Just store number.ignore M,C,A initials.
//for oxygen it will store 10,20,00.Just store number.ignore M,C,A initials.
}
public class proportion
{
public int Number { get; set; }
}
This is what i have done:
List<Planets> Planets = new List<Planets>();
using (StreamReader sr = new StreamReader(args[0]))
{
String line;
while ((line = sr.ReadLine()) != null)
{
string[] parts = Regex.Split(line, #"(?<=[|;-])");
foreach (var item in parts)
{
var Obj = new Planets();//Not getting how to store it but not getting proper output in parts
}
Console.WriteLine(line);
}
}
Without you having to change any of your logic in "Planets"-class my fast solution to your problem would look like this:
List<Planets> Planets = new List<Planets>();
using (StreamReader sr = new StreamReader(args[0]))
{
String line;
while ((line = sr.ReadLine()) != null)
{
Planets planet = new Planets();
String[] parts = line.Split('|');
planet.Number = Convert.ToInt32(parts[0]);
planet.name = parts[1];
planet.obj = parts[2];
String[] smallerParts = parts[3].Split(';');
planet.proportion = new List<proportion>();
foreach (var item in smallerParts)
{
proportion prop = new proportion();
prop.Number =
Convert.ToInt32(item.Split(':')[1]);
planet.proportion.Add(prop);
}
Planets.Add(planet);
}
}
Oh before i forget it, you should not name your property of class Planets "object" because "object" is a keyword for the base class of everything, use something like "obj", "myObject" ,"planetObject" just not "object" your compiler will tell you the same ;)
To my understanding, multiple delimiters are maintained to have a nested structure.
You need to split the whole string first based on pipe, followed by semi colon and lastly by colon.
The order of splitting here is important. I don't think you can have all the tokens at once by splitting with all 3 delimiters.
Try following code for same kind of data
var values = new List<string>
{
"50|Hallogen|Mercury|M:4;C:40;A:1",
"90|Oxygen|Mars|M:10;C:20;A:00",
"5|Hydrogen|Saturn|M:33;C:00;A:3"
};
foreach (var value in values)
{
var pipeSplitted = value.Split('|');
var firstNumber = pipeSplitted[0];
var name = pipeSplitted[1];
var objectName = pipeSplitted[2];
var semiSpltted = value.Split(';');
var secondNumber = semiSpltted[0].Split(':')[1];
var thirdNumber = semiSpltted[1].Split(':')[1];
var colenSplitted = value.Split(':');
var lastNumber = colenSplitted[colenSplitted.Length - 1];
}
The most straigtforward solution is to use a regex where every (sub)field is matched inside a group
var subjectString = #"50|Hallogen|Mercury|M:4;C:40;A:1
90|Oxygen|Mars|M:10;C:20;A:00
5|Hydrogen|Saturn|M:33;C:00;A:3";
Regex regexObj = new Regex(#"^(.*?)\|(.*?)\|(.*?)\|M:(.*?);C:(.*?);A:(.*?)$", RegexOptions.Multiline);
Match match = regexObj.Match(subjectString);
while (match.Success) {
match.Groups[1].Value.Dump();
match.Groups[2].Value.Dump();
match.Groups[3].Value.Dump();
match.Groups[4].Value.Dump();
match.Groups[5].Value.Dump();
match.Groups[6].Value.Dump();
match = match.NextMatch();
}
If I understand correctly, your input is well formed. In this case you could use something like this:
string[] parts = Regex.Split(line, #"[|;-]");
var planet = new Planets(parts);
...
public Planets(string[] parts) {
int.TryParse(parts[0], this.Number);
this.name = parts[1];
this.object = parts[2];
this.proportion = new List<proportion>();
Regex PropRegex = new Regex("\d+");
for(int i = 3; i < parts.Length; i++){
Match PropMatch = PropRegex.Match(part[i]);
if(PropMatch.IsMatch){
this.proportion.Add(int.Parse(PropMatch.Value));
}
}
}
I have a dictionary:
Dictionary<ICD_Map2, string> maps = new Dictionary<ICD_Map2, string>();
I add to the dictionary via button click:
private void button2_Click(object sender, EventArgs e)
{
maps.Clear();
// Load mapping file.
var reader = new StreamReader(File.OpenRead(#"Call_Details_Map.csv"));
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
maps.Add(new ICD_Map2(values[0].Replace("\"",""), values[1].Replace("\"","")), values[2].Replace("\"",""));
}
}
I want to use LINQ and map my keys to the "string" in maps.
How do I do it?
var File001 = from line in File.ReadLines(ICD_process)
let l = line.Split(',')
where l[0] != "\"Statement Date\""
select new
{
CallType = maps.ToLookup(p => l[5], p => l[3]),
Calls = l[11] == "\"\"" ? "0" : (maps.ToLookup(p => l[5], p => l[3]) == "Mobile Data" || maps.ToLookup(p => l[5], p => l[3]) == "Mobile SMS") ? "0" : l[11].Replace("\"","").ToString())
};
I am getting error in Calls variable in File001 Linq method
It's not clear what you are trying to achieve, but here is my advice. Now you are working with spitted lines array like this l[0] != "\"Statement Date\"". I think only you know what data should be at index 9. It's not very readable, error-prone (typo in Statemnet Date, wrong index), and it's very hard to maintain. Instead this create an object, which will parse line for you and provide data via strongly typed properties with nice names.
public class ICDEntry
{
public static ICDEntry CreateFrom(string line)
{
string[] values = line.Split(',');
var entry = new ICDEntry();
// assign values to properties:
// if (values[0] == "\"Statement Date\"")
// entry.StatementDate = DateTime.Parse(values[1]);
// entry.IsSomething = values[11] == "\"\""
return entry;
}
public DateTime? StatementDate { get; private set; }
public string MobileSMS { get; private set; }
public bool IsSomething { get; private set; }
}
Now you can parse each line, and then work in strongly typed world making queries to your ICD entries:
var entries = File.ReadLines(ICD_process).Select(l => ICDEntry.CreateFrom(l));
var File001 = from e in entries
where e.StatementDate.HasValue
select new {
Calls = e.IsSomething ? "0" : e.MobileSMS; // use ICDEntry here
};