Parse string into multiple variables - c#

I am working on an application that is pulling a list of all file names from a specific directory and needs to then parse the file name into multiple variables to then be submitted to a database.
How can I parse the string file name into multiple variables?
EX Files:
2014_31_12_09_36AM_15555555555_108
2014_31_12_09_39AM_108_15555555555
2014_31_12_09_17AM_102_108
The file name contains year, day, month, hour, minutes (with AM/PM), and a 3 digit or 11 digit number, followed by another 3 or 11 digit number
All file names are stored in an array after the directory is scanned.
private void ParseFileNames()
{
string Year = "";
string Day = "";
string Month = "";
string Hour = "";
string Minute = "";
string Called = "";
string Calling = "";
//Loop through scanned file names and parse them one at a time.
for (int i = 0; i < fileNames.Length; i++)
{
string[] parsedFileName = fileNames[i].Split('_');
Year = parsedFileName[0];
Day = parsedFileName[1];
Month = parsedFileName[2];
Hour = parsedFileName[3];
Minute = parsedFileName[4];
Called = parsedFileName[5];
Calling = parsedFileName[6];
//open DB connection and submit each individual parsed file data into DB
//Move file from toIndexPath to IndexedPath
}
}
Is there a better way to do this?

you could return a class like this
public class FileData
{
public int Year {get;set;}
public int Day {get;set;}
public int Month {get;set;}
public int Hour {get;set;}
public int Minute {get;set;}
public int Called {get;set;}
public int Calling {get;set;}
public FileData(string[] parsedInfo)
{
Year = Convert.ToInt32(parsedInfo[0]);
// and so on with other member to load
}
}
then simply call the following to get a List<FileData> :
var listFileData = fileNames.Select(fn=> new FileData(fn.Split('_'))).ToList();

This code is shorter, I dont know about better, but at least you get a Date Time
//This code is just for testing the strings, delete these 4 after testing.
var stringList = new List<string>();
stringList.Add("2014_31_12_09_36AM_15555555555_108");
stringList.Add("2014_31_12_09_39AM_108_15555555555");
stringList.Add("2014_31_12_09_17AM_102_108");
var allFileDate = new List<FileData>();
foreach (var item in stringList)
{
var dt = item.Split('_');
var timeCombined = string.Concat(dt[2], "/", dt[1],"/", dt[0]," ", dt[3], ":", dt[4]);
allFileDate.Add(new FileData {Date = Convert.ToDateTime(timeCombined), Called = dt[5], Calling = dt[6]});
}
And your class
public class FileData
{
public DateTime Date { get; set; }
public string Called { get; set; }
public string Calling { get; set; }
}
All data validation in there and it's alright.

Is there a better way to do this?
'better' is pretty subjective. there's always a better way depending on what you want. In your case I would not be writing code to parse your filenames at all, and you'd be making a lot of round trips to populate your table one row at a time.
I would instead just use a bulk load command, e.g.: LOAD DATA ... INTO TABLE ... LINES TERMINATED BY '\r\n' FIELDS TERMINATED BY '_'

Related

How to sort data stored in a text file by high score in c#

I am making a quiz game for my computing coursework and I have completed the quiz part of my application and I am currently moving on to my leader board but to do that I need to sort my scores text file which contains the users name, score and time but I am now trying to retrieve these data and display them in a table.
The users' name score and time saves fine into the scores text file but to display these details I first need to order these details by the score.
My test file is organised like this:
username,score,time
userName1,33,12
userName2,-55,33
userName3,34,2
userName4,23,27
userName5,63,72
This is the code that I am currently using but this only works if I have the data in the text file sorted first.
string[] readFile = File.ReadAllLines(file).ToArray();
for (int i = 0; i < 5; i++)
{
string[] userDetails = File.ReadAllLines(file).ToArray();
string username = userDetails[0];
string score = userDetails[1];
// Apply the text of lblUsername1-5 to be what the names
// of the top 5 scorers are in the file.
lblUsername1.Text = userDetails[0].Split(',')[0];
lblUsername2.Text = userDetails[1].Split(',')[0];
lblUsername3.Text = userDetails[2].Split(',')[0];
lblUsername4.Text = userDetails[3].Split(',')[0];
lblUsername5.Text = userDetails[4].Split(',')[0];
// Apply the text of lblScore1-5 to be what the scores
// of the top 5 scorers are in the file.
lblScore1.Text = userDetails[0].Split(',')[1];
lblScore2.Text = userDetails[1].Split(',')[1];
lblScore3.Text = userDetails[2].Split(',')[1];
lblScore4.Text = userDetails[3].Split(',')[1];
lblScore5.Text = userDetails[4].Split(',')[1];
}
So if some one could help me to sort the data in my scores ext file that would be great. Thanks in advance.
You can use linq to sort data from your file
string[][] userDetails = File.ReadAllLines(file).Select(s => s.Split(',')).OrderBy(arr => int.TryParse(arr[1], out int result) ? result : 0)).Take(5).ToArray();
lblUsername1.Text = userDetails[0][0];
lblUsername2.Text = userDetails[1][0];
lblUsername3.Text = userDetails[2][0];
lblUsername4.Text = userDetails[3][0];
lblUsername5.Text = userDetails[4][0];
// Apply the text of lblScore1-5
// to be what the scores of the top 5 scorers are in the file.
lblScore1.Text = userDetails[0][1];
lblScore2.Text = userDetails[1][1];
lblScore3.Text = userDetails[2][1];
lblScore4.Text = userDetails[3][1];
lblScore5.Text = userDetails[4][1];``
You should use objects to manage this. Your class should be a user with properties, se below.
Now you have full control on sorting and managing of your objects
using System.Collections.Generic;
using System.Linq;
public class User
{
public string Name { get; set; }
public int Score { get; set; }
public int Time { get; set; }
}
class Program
{
public static void Main(string[] args)
{
//This you get from file, no need for this in your code
string[] fromFile = new string[5]
{ "userName1,33,12", "userName2,-55,33", "userName3,34,2", "userName4,23,27", "userName5,63,72" };
List<User> users = new List<User>();
foreach (string line in fromFile)
{
string[] splitLine = line.Split(',');
users.Add(new User() { Name = splitLine[0], Score = int.Parse(splitLine[1]), Time = int.Parse(splitLine[2]) });
}
foreach (User oneUser in users.OrderByDescending(x => x.Score))
{
//Here the store to file or what you want to do
}
}
}

Output data to CSV specific columns from Dictionary c#

I am trying to output the values from the dictionary to the CSV and am able to do this. But facing issue with the specific columns this need to output to the csv. I need the specific data value from dictionary to be output to a specific column in csv.
Dictionary<string, List<string>> file = new Dictionary<string, List<string>>();
for (var i = 0; i < stringList.Count(); i++)
{
string line = stringList[i];
string path = line.Replace("\r\n", "");
path = path.Replace(" ", "");
path = path.TrimEnd(':');
if (File.Exists(path))
{
file[path] = file.ContainsKey(path) ? file[path] : new List<string>();
for (var j = i + 1; j < stringList.Count(); j++)
{
string line2 = stringList[j];
string path2 = line2.Replace("\r\n", "");
path2 = path2.Replace(" ", "");
path2 = path2.TrimEnd(':');
if (File.Exists(path2))
{
i = j - 1;
break;
}
else
{
if (path2.Contains("Verified") | path2.Contains("Algorithm"))
{
var strings = path2.Split(':');
var listValue = strings[1].Trim();
file[path].Add(listValue);
}
}
}
}
}
using (var writer = new StreamWriter(outputdir + "\\output_" +
DateTime.Now.ToString("yyyy_MM_dd_HHmmss") + ".csv"))
{
writer.WriteLine("FilePath,Signature,HashValueSHA1, HashValueSHA2, HashValueMD5, Other");
foreach (var keyvaluepair in file)
{
if (!keyvaluepair.Value.Contains("Unsigned"))
{
var values = String.Join(",", keyvaluepair.Value.Distinct().Select(x => x.ToString()).ToArray());
writer.WriteLine("{0},{1}", keyvaluepair.Key, values);
}
}
}
Current Output looks like below:
Sample output I need as below:
The Dictionary key(string) would hold the file path and the values(List) would hold something like below:
Signed
sha1RSA
md5RSA
md5RSA
Signed
sha1RSA
sha1RSA
sha256RSA
sha256RSA
Please suggest how can I get the one as required output.
I had a longer answer typed, but I see the problem.
On this line
var values = String.Join(",", keyvaluepair.Value.Distinct().Select(x => x.ToString()).ToArray());
take out Distinct. It looks like you have the correct number of items in each string, but if a list contains multiple blank entries Distinct is eliminating the duplicates. If a list contains two or three blank entries you need all of them. If you delete duplicate blanks your columns won't line up.
Also, when you use Distinct there's no guarantee that items will come back in any particular order. In this case the order is very important so that values end up in the right columns.
So in your example above, even though there's a blank in the third column of the first row, the value from the fourth column ends up in the third column and the blank goes to the end.
That will likely fix the immediate problem. I'd recommend not using a List<string> when you're expecting a certain number of values (they need to match up with columns) because a List<string> can contain any number of values.
Instead, try something like this:
public class WhateverThisIs
{
public string Signature { get; set; }
public string HashValueSha1 { get; set; }
public string HashValueSha2 { get; set; }
public string HashValueMd5 { get; set; }
public string Other { get; set; }
}
Then, as a starting point, use Dictionary<string, WhateverThisIs>.
Then the part that outputs lines would look more like this:
var value = keyvaluepair.Value;
var values = String.Join(",", value.Signature, value.HashValueSha1, value.HashValueSha2,
value.HashValueMd5, value.Other);
(and yes, that accounts for null values.)
If you want to replace nulls or empty values with "N/A" then you'd need a separate function for that, like
string ReplaceNullWithNa(string value)
{
return string.IsNullOrEmpty(value) ? "N/A" : value;
}

C# - how to monitor transaction activity for a banking application

I have been trying on a banking application code in which I want to monitor transaction activity where you read through a list of transaction records and return account nos that have exceeded the threshold amount for a given date.
For this I created a dummy text file using streamwriter where in I have the transaction records maintained spaced out with \t
For eg: AccountNumber\tTransactionDate\tTransactionAmount
When I read I am not sure how to split and collect distinct account nos for a given date and calculate the transaction amounts to further verify if its above the threshold amount.
Any help with this will be highly appreciated.
Thanks in advance.
You could split out the tabs to an array using split on the string you've read.
String Instr = "AccountNumber\tTransactionDate\tTransactionAmount";
char delim = '\t';
string[] array = Instr.Split(delim);
// array[0] = AccountNumber
// array[1] = TransactionDate
// array[2] = TransactionAmount
To build upon what Steve said, I would personally be inclined to deserialize the text into an object. Doing something like what I have below....
Now, instead of having just an array of text you could have an array of objects whose properties are the right types. Lots easier to do a date comparison... add up all of the amounts for a particular account when they're in plain old objects. At least, for me it is.
As a side note, what I have below is a pretty bad idea to do. But if you're just stubbing something out to replace it with something like a database access layer in the future, this could work in the short term.
Edit: If you're going to be storing data in plain text files, it would be much better to store them in a format that C# can handle. Something like JSON, and then use Newtonsoft.Json to serialize / deserialize or XML and use System.Xml.Serialization to serialize / deserialize the data.
class Program
{
static void Main(string[] args)
{
string srcfile = #"C:\Workspace\tmp\TestSTuff\bank\transactions.txt";
string transactionstr;
using (FileStream fs = new FileStream(srcfile, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[fs.Length];
int numtoread = (int)fs.Length;
int numread = 0;
while (numtoread > 0)
{
int n = fs.Read(buffer, numread, numtoread);
if (n == 0)
break;
numread += n;
numtoread -= n;
}
transactionstr = Encoding.Default.GetString(buffer);
}
char[] newline = { '\r','\n' };
char delim = ',';
string[] transactionstrs = transactionstr.Split(newline);
List<Transaction> transactions = new List<Transaction>();
foreach (var t in transactionstrs)
{
try
{
string[] fields = t.Split(delim);
DateTime.Parse(fields[1]);
transactions.Add(new Transaction
{
AccountNumber = int.Parse(fields[0]),
TransactionDate = DateTime.Parse(fields[1]),
TransactionAmount = double.Parse(fields[2])
});
}
catch
{
continue;
}
}
}
}
public class Transaction
{
public int AccountNumber { get; set; }
public DateTime TransactionDate { get; set; }
public double TransactionAmount { get; set; }
}

What is the easiest way to split columns from a txt file

I've been looking around a bit but haven't really found a good example with what I'm struggling right now.
I have a .txt file with a couple of columns as follows:
# ID,YYYYMMDD, COLD,WATER, OD, OP,
52,20120406, 112, 91, 20, 130,
53,20130601, 332, 11, 33, 120,
And I'm reading these from the file into a string[] array.
I'd like to split them into a list
for example
List results, and [0] index will be the first index of the columns
results[0].ID
results[0].COLD
etc..
Now I've been looking around, and came up with the "\\\s+" split
but I'm not sure how to go about it since each entry is under another one.
string[] lines = File.ReadAllLines(path);
List<Bus> results = new List<Bus>();
//Bus = class with all the vars in it
//such as Bus.ID, Bus.COLD, Bus.YYYYMMDD
foreach (line in lines) {
var val = line.Split("\\s+");
//not sure where to go from here
}
Would greatly appreciate any help!
Kind regards, Venomous.
I suggest using Linq, something like this:
List<Bus> results = File
.ReadLines(#"C:\MyFile.txt") // we have no need to read All lines in one go
.Skip(1) // skip file's title
.Select(line => line.Split(','))
.Select(items => new Bus( //TODO: check constructor's syntax
int.Parse(items[1]),
int.Parse(items[3]),
DateTime.ParseExact(items[2], "yyyyMMdd", CultureInfo.InvariantCulture)))
.ToList();
I would do
public class Foo
{
public int Id {get; set;}
public string Date {get; set;}
public double Cold {get; set;}
//...more
}
Then read the file
var l = new List<Foo>();
foreach (line in lines)
{
var sp = line.Split(',');
var foo = new Foo
{
Id = int.Parse(sp[0].Trim()),
Date = sp[1].Trim(),//or pharse the date to a date time struct
Cold = double.Parse(sp[2].Trim())
}
l.Add(foo);
}
//now l contains a list filled with Foo objects
I would probably keep a List of properties and use reflection to populate the object, something like this :
var columnMap = new[]{"ID","YYYYMMDD","COLD","WATER","OD","OP"};
var properties = columnMap.Select(typeof(Bus).GetProperty).ToList();
var resultList = new List<Bus>();
foreach(var line in lines)
{
var val = line.Split(',');
var adding = new Bus();
for(int i=0;i<val.Length;i++)
{
properties.ForEach(p=>p.SetValue(adding,val[i]));
}
resultList.Add(adding);
}
This is assuming that all of your properties are strings however
Something like this perhaps...
results.Add(new Bus
{
ID = val[0],
YYYYMMDD = val[1],
COLD = val[2],
WATER = val[3],
OD = val[4],
OP = val[5]
});
Keep in mind that all of the values in the val array are still strings at this point. If the properties of Bus are typed, you will need to parse them into the correct types e.g. assume ID is typed as an int...
ID = string.IsNullOrEmpty(val[0]) ? default(int) : int.Parse(val[0]),
Also, if the column headers are actually present in the file in the first line, you'll need to skip/disregard that line and process the rest.
Given that we have the Bus class with all the variables from your textfile:
class Bus
{
public int id;
public DateTime date;
public int cold;
public int water;
public int od;
public int op;
public Bus(int _id, DateTime _date, int _cold, int _water, int _od, int _op)
{
id = _id;
date = _date;
cold = _cold;
water = _water;
od = _od;
op = _op;
}
}
Then we can list them all in the results list like this:
List<Bus> results = new List<Bus>();
foreach (string line in File.ReadAllLines(path))
{
if (line.StartsWith("#"))
continue;
string[] parts = line.Replace(" ", "").Split(','); // Remove all spaces and split at commas
results.Add(new Bus(
int.Parse(parts[0]),
DateTime.ParseExact(parts[1], "yyyyMMdd", CultureInfo.InvariantCulture),
int.Parse(parts[2]),
int.Parse(parts[3]),
int.Parse(parts[4]),
int.Parse(parts[5])
));
}
And access the values as you wish:
results[0].id;
results[0].cold;
//etc.
I hope this helps.

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! :)

Categories