Read delimited text files dynamically - c#

I want to read a textfile dynamically based on the headers. Consider an example like this
name|email|phone|othername|company
john|john#example.com|1234||example
doe|doe#example.com||pin
jane||98485|
The values to be read like this for the following records
name email phone othername company
john john#example.com 1234 example
doe doe#example.com pin
jane 98485
I tried using this
using (StreamReader sr = new StreamReader(new MemoryStream(textFile)))
{
while (sr.Peek() >= 0)
{
string line = sr.ReadLine(); //Using readline method to read text file.
string[] strlist = line.Split('|'); //using string.split() method to split the string.
Obj obj = new Obj();
obj.Name = strlist[0].ToString();
obj.Email = strlist[1].ToString();
obj.Phone = strlist[2].ToString();
obj.othername = strlist[3].ToString();
obj.company = strlist[4].ToString();
}
}
Above code works if all the delimiters are put exactly but doesn't work when given dynamically like the above. Any possible solution for this?

If you have any control over this, you should use a better serialization techinology, or at least use a csv parser that can deal with this sort of format. However, if you want to use string.Split, you can also take advantage of ElementAtOrDefault
Returns the element at a specified index in a sequence or a default
value if the index is out of range.
Given
public class Data
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string OtherName { get; set; }
public string Company { get; set; }
}
Usage
var results = File
.ReadLines(SomeFileName) // stream the lines from a file
.Skip(1) // skip the header
.Select(line => line.Split('|')) // split on pipe
.Select(items => new Data() // populate some funky class
{
Name = items.ElementAtOrDefault(0),
Email = items.ElementAtOrDefault(1),
Phone = items.ElementAtOrDefault(2),
OtherName = items.ElementAtOrDefault(3),
Company = items.ElementAtOrDefault(4)
});
foreach (var result in results)
Console.WriteLine($"{result.Name}, {result.Email}, {result.Phone}, {result.OtherName}, {result.Company}");
Output
john, john#example.com, 1234, , example
doe, doe#example.com, , pin,
jane, , 98485, ,

When you split the line like string[] strlist = line.Split('|'); you can get undesired results.
For example: jane||98485| generates an array of just 4 elements as you can check here https://rextester.com/WBOT6074 online.
You should check your array strList after generating it with thinks like measuring the size.
As you haven't given clear details about the problem I cannot give a more especific answer to it.

Related

MVVM get data from text file

I can not get the logic how to search in a text file and then get the data I need using model view view model.
Basically, I have to make a dictionary app and I have word,language and description in the text file. Like:
cat;e English; it is a four leg animal
In the model I have a text box where the client writes a word and two other boxes, where language and description of the word should be shown.
I just can not get how to search in this file. I tried to search online but nothing seemed to meet my exact question.
Unless your file is going to change you can get away with reading the entire file up front when running your application and putting the data into lists of models for your view models.
As this is essentially a CSV file, and assuming each entry is a line, using a Semi-colon as the delimiter we can use the .Net CSV parser to process your file into your models:
Basic Model:
public class DictionaryEntryModel {
public string Word { get; set; }
public string Language { get; set; }
public string Description { get; set; }
}
Example view model with a constructor to fill out your models:
public class DictionaryViewModel {
// This will be a INotify based property in your VM
public List<DictionaryEntryModel> DictionaryEntries { get; set; }
public DictionaryViewModel () {
DictionaryEntries = new List<DictionaryEntryModel>();
// Create a parser with the [;] delimiter
var textFieldParser = new TextFieldParser(new StringReader(File.ReadAllText(filePath)))
{
Delimiters = new string[] { ";" }
};
while (!textFieldParser.EndOfData)
{
var entry = textFieldParser.ReadFields();
DictionaryEntries.Add(new DictionaryEntryModel()
{
Word = entry[0],
Language = entry[1],
Description = entry[2]
});
}
// Don't forget to close!
textFieldParser.Close();
}
}
You can now bind your view using the property DictionaryEntries and as long as your app is open it will preserve your full file as the list of DictionaryEntryModel.
Hope this helps!
I'm not addressing the MVVM part here, but just how to search the text file in order to get resulting data according to a search term, using case insensitive regex.
string dictionaryFileName = #"C:\Test\SampleDictionary.txt"; // replace with your file path
string searchedTerm = "Cat"; // Replace with user input word
string searchRegex = string.Format("^(?<Term>{0});(?<Lang>[^;]*);(?<Desc>.*)$", searchedTerm);
string foundTerm;
string foundLanguage;
string foundDescription;
using (var s = new StreamReader(dictionaryFileName, Encoding.UTF8))
{
string line;
while ((line = s.ReadLine()) != null)
{
var matches = Regex.Match(line, searchRegex, RegexOptions.IgnoreCase);
if (matches.Success)
{
foundTerm = matches.Groups["Term"].Value;
foundLanguage = matches.Groups["Lang"].Value;
foundDescription = matches.Groups["Desc"].Value;
break;
}
}
}
Then you can display the resulting strings to the user.
Note that this will work for typical input words, but it might produce strange results if the user inputs special characters that interfere with the regular expression syntax. Most of this might be corrected by utilizing Regex.Escape(searchedTerm).

Better method for loading a list of items to be parsed into two, and calling the matching item by index?

I have a .txt file with a list of items (u.s. state and capitals) going down such as Arizona:Phoenix Arkansas:Little Rock California:Sacramento. I'm going to be importing that list, but only want to display the States in a Combobox. After that, if comboBox1.Items[0] is selected, I want it to get the corresponding item that was initially parsed along with it after the : delimiter. My initial solution was to create a class to hold both values, and hold them in a List and compare the index from the Combobox to that of the List to get the matching value. I feel like this might be overkill and I am over thinking it for something as simple as a combobox where the data won't be subjected to any complex manipulations. Would there be a simpler method/datatype to use to do this? I just want to get the corresponding value after the : delimiter from the Combobox index that was parsed when it was first loaded.
First of all build your classes of State & Capital like this:
public class State
{
public string stateName { get; set; }
public Capital capital { get; set; }
}
public class Capital
{
public string capitalName { get; set; }
}
Read the text file, generate a list and populate the ComboBox like this:
List<State> list = new List<State>();
var file = File.ReadAllLines(FilePath).ToList();
foreach (var item in file)
list.Add(new State()
{
stateName = item.Split(':')[0],
capital = new Capital() { capitalName = item.Split(':')[1] }
});
StatesCB.DataSource = list.Select(x => x.stateName).ToList();
And within your ComboBoxIndexChange eventHandler, get the Capital based on the State.
private void Sates_SelectedIndexChanged(object sender, EventArgs e)
{
capital.Text = list.Where(x => x.stateName == StatesCB.SelectedValue)
.Select(x => x.capital.capitalName).FirstOrDefault();
}
It works and address your problem perfetcly.
You can try this:
I assume your text file contains the following lines:
Arizona:Phoenix
Arkansas:Little
Rock California:Sacramento
On your code:
List<string> lstResult = new List<string>();
using (StreamReader sr = new StreamReader(#"C:\Stack\file.txt"))
{
string line = string.Empty;
while ((line = sr.ReadLine()) != null)
{
//Here I am getting the second part of splitted string which is your requirement
lstResult.Add(line.Split(':').Select(x=>x).Skip(1).SingleOrDefault().ToString());
}
}
comboBox1.DataSource = lstResult;
This will produce:

Reading from .txt file, then exporting data to DataGridView

I know this is a laughable question, but God, I've spent my entire last day banging my head with it and it just won't work! The goddamn teacher didn't even mention anything about importing any data into DataGridView!
I have a C# Windows Forms homework assignment: I have to read data from a .txt (users) file and paste it into a DataGridView table in C# Microsoft Visual Studio 2012. The data in the users.txt file is something like that with TAB delimiters:
-------------------------------------------------
ID Name Surname Telephone VIP Age Balance
-------------------------------------------------
0001 John Killer 1-500-300 0 13 2272
0002 Name Surname 1-500-200 0 27 225
0003 Martin King 1-500-400 1 41 1070
Ignore the label names (ID, Name, Surname...), I wrote them only for clarity, the real file has only the raw user data in it.
Now, I have previously created a class Users, which has these fields:
ID
Name
Surname
Telephone
VIP
Bought items
Price
and then created a DataGridView (usersDataGridView) and imported the fields from class Users in it.
OK, algorithmically this is somewhat easy task, ain't it?
My idea was doing the following: reading the file content with a StreamReader, saving each line to a string, then splitting the string into parts using \t as a delimiter with String.Split.
However, once I got those lines split... well, I basically have no idea how to import them into the DataGridView (I "know" it should be as a DataSource but... the Visual Studio 2012's UI seems way too "complicated" for me to let me figure out how I can point a string or whatever goddamn data type it is as a DataSource).
My pitiful attempts had led me to the following:
Attempt 1:
public void Test_1()
{
string filePath = string.Format("{0}/databases/{1}", AppDomain.CurrentDomain.BaseDirectory, "user_db.txt");
string[] textData = System.IO.File.ReadAllLines(filePath);
string[] headers = textData[0].Split('\t');
DataTable dataTable1 = new DataTable();
foreach (string header in headers)
dataTable1.Columns.Add(header, typeof(string), null);
for (int i = 1; i < textData.Length; i++)
dataTable1.Rows.Add(textData[i].Split('\t'));
//Set the DataSource of DataGridView to the DataTable
promotionsDataGridView.DataSource = dataTable1;
}
Attempt 2:
public void ReadFromFile()
{
string delimeter = "\t";
string tableName = "BooksTable";
string fileName = string.Format("{0}/databases/{1}", AppDomain.CurrentDomain.BaseDirectory, "bigtest.sql");
DataSet dataset = new DataSet();
StreamReader sr = new StreamReader(fileName);
dataset.Tables.Add(tableName);
dataset.Tables[tableName].Columns.Add("InventoryID");
dataset.Tables[tableName].Columns.Add("Brand");
dataset.Tables[tableName].Columns.Add("Category");
dataset.Tables[tableName].Columns.Add("Description");
dataset.Tables[tableName].Columns.Add("Promotions");
dataset.Tables[tableName].Columns.Add("Quantity");
dataset.Tables[tableName].Columns.Add("Price");
string allData = sr.ReadToEnd();
string[] rows = allData.Split("\r".ToCharArray());
foreach (string r in rows)
{
string[] items = r.Split(delimeter.ToCharArray());
dataset.Tables[tableName].Rows.Add(items);
}
this.productsDataGridView.DataSource = dataset.Tables[0].DefaultView;
}
However I keep getting some bullshit error like
Input array size is bigger than the whatever
Since I have literally no experience with DataGridView I guess I have some terrible mistakes at algorhitmic level, right?!
Please, anyone, help me! I have read, copied, pasted, compiled and debugged from like 20 different issues on similar topic and I am still at nowhere!
What's the proper way of reading data from a .txt file, then pasting it to a DataGridView?
Any help or answers are deeply appreciated!!
If you already have a User object defined you can add a static method to it that loads the data to a list of users and bind it to the grid view.
public class User {
public string Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Telephone { get; set; }
public bool Vip { get; set; }
public int Age { get; set; }
public decimal Balance { get; set; }
public static List<User> LoadUserListFromFile(string path) {
var users = new List<User>();
foreach (var line in File.ReadAllLines(path)) {
var columns = line.Split('\t');
users.Add(new User {
Id = columns[0],
Name = columns[1],
Surname = columns[2],
Telephone = columns[3],
Vip = columns[4] == "1",
Age = Convert.ToInt32(columns[5]),
Balance = Convert.ToDecimal(columns[6])
});
}
return users;
}
}
Then you can simply load that into a data grid:
usersDataGridView.DataSource = User.LoadUserListFromFile("user_db.txt");

Parse file with multiples values lines C#

I have to parse a file that is constructed like this :
User: jcruz Name: Jules Last: Cruz Email: Some#email.com
User: jdoe Name: John Last: Doe Email: Some#email.com
User: pmartin Name: Pete Last: Martin Email: Some#email.com
User: rrichard Name: Reed Last: Richard Email: Some#email.com
I need to split every line taking just Name, Last Name and Email into an object of the type
var contact = new Conctact {
Name = fieldFromLine,
Last= fieldFromLine,
Email = fieldFromLine
}
So my problem is which tool use : String.Split or Regex.Split. and how to implement it.
Thank you very much...
This is what a Have done so far:
String archivo = ((FileDialog)sender).FileName;
using (TextReader sr = new StreamReader(archivo,Encoding.UTF8))
{
String line = String.Empty;
while ((line = sr.ReadLine()) != null )
{
string[] result = Regex.Split(line,"User:");
//How to get the other fields...
}
}
var result =File.ReadLines(fileName)
.Select(line => line.Split(new string[]{"User:", "Name:", "Last:", "Email:"}, StringSplitOptions.RemoveEmptyEntries))
.Select(parts => new Conctact(){ Name = parts[1], Last = parts[2], Email = parts[3] })
.ToArray();
try this:
public class contact
{
public string Name { get; set; }
public string Lname { get; set; }
public string Email { get; set; }
}
List<contact> contact = new List<contact>();
private void split()
{
var lines = File.ReadAllLines(#"txt file address");
foreach (var line in lines)
{
var splitline=line.Split(':');
string name = splitline[2].Replace("Last", "");
string lname = splitline[3].Replace("Email","");
contact.Add(new contact { Name = name, Lname = lname, Email = splitline[4] });
}
}
Answer: neither.
Use a simple finite-state machine parser to read the file because unless you can guarantee that the text values will never be "Name:" or "Last:" or "Email:" then you'll run into problems with string splitting. Also FSM-based parsers are significantly faster than string splitting (as there is no extraneous string allocation).
I don't have the time to write out an entire parser, but here's the simple logic:
enum State { InUser, InName, InLast, InEmail }
State currentState = State.InUser; // you start off with the 'cursor' in the "User" section
StringBuilder sb = new StringBuilder(); // this holds the current string element
foreach(Char c in entireTextFile) { // presumably using `StreamReader.Read()`
switch( currentState ) {
case InUser:
switch( c ) {
// state transition logic here
}
// append the character to the StringBuilder until you've identified and reached the next field, then save the sb value to the appropriat
case InName:
// and so on...
}
}
Of course, an FSM parser is fundamentally the same thing as a Regular Expression parser, but it means you get to code the state-transitions yourself rather than using RegEx's syntax which is faster, performance-wise.
If your project is small and don't care about performance, and can guarantee certain data formatting rules then I'd go with regex.
But never, ever, use String.Split to read a file.
Regex is overkill. Also note that some last names that contain spaces.
Contact c = new Contact();
string () tokens = input.Split(":".ToCharArray());
if (tokens.Count < 5)
return; // error
// now strip the last word from each token
c.Name = tokens(2).Substring(0, tokens(2).LastIndexOf(" ".ToCharArray())).Trim();
c.Last = tokens(3).Substring(0, tokens(3).LastIndexOf(" ".ToCharArray())).Trim();
c.Email = tokens(4).Trim();

Splitting a list<> that each item contains comma separated strings and formatting it out

I have a simple class like this:
class QuickReport
{
public string DeviceName { get; set; }
public string GroupName { get; set; }
public string PinName { get; set; }
public override string ToString()
{
return DeviceName + "," + GroupName + "," + PinName;
}
}
Later I make a list of items with this class:
List<QuickReport> QR = new List<QuickReport>();
Later in my program it will fill up and when I save it in a text file it will be like this example:
HBM\D1,GND,10
HBM\D1,GND,12
HBM\D1,NT_IOp,115
HBM\D1,NT_IOp,117
HBM\D2,GND,8
HBM\D2,NT_IOp,115
HBM\D2,NT_IOp,116
Now I want to make a function to save the text file in more readable manner. That is formatting it by DEVICE, GROUPS and PINS. So the above example would result in:
HBM\D1
GND: 10, 12
NT_IOp: 115, 117
HBM\D2
GND: 8
NT_IOp: 115, 116
can you please help and give some ideas?
Thanks!
var query = QR.ToLookup(i=>i.DeviceName, i => new {i.GroupName, i.PinName})
.Select(i=>
new {DeviceName = i.Key,
Groups = i.ToLookup(g=>g.GroupName, g=>g.PinName)});
var sb = new StringBuilder();
foreach ( var device in query)
{
sb.AppendLine(device.DeviceName);
foreach ( var gr in device.Groups)
{
sb.Append(gr.Key + ": ");
sb.Append(String.Join(", ", gr.ToArray()));
sb.AppendLine();
}
sb.AppendLine();
}
var stringToWrite = sb.ToString();
As i understand you have tree structure, where Device have child Groups, and Groups have child pins.
You can create custom classes like this:
class Group
{
string Name;
//pins that belong to this group
List<string> pins;
}
class Device
{
string Name;
//groups that belong to this device
List<Group> Groups;
}
And than just collect it to List<Device> and serialize it using XML Serialization.
This isn't complete, but it should give you enough to go on. You'll still need to add your newlines, and remove trailing commas, etc.
// Make your key the device name
var qrHash = new Dictionary<string, List<QuickReport>>();
// Populate your QR Dictionary here.
var output = new StringBuilder();
foreach (var keyValuePair in qrHash)
{
output.Append(keyValuePair.Key);
var gnd = new StringBuilder("GND: ");
var nt = new StringBuilder("NT_IOp: ");
foreach (var qr in keyValuePair.Value)
{
gnd.Append(qr.GroupName);
nt.Append(qr.PinName);
}
output.Append(gnd);
output.Append(nt);
}
How about using the XmlSerializer to serialize and deserialize your class? This should provide some readable output.
http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx
The quickest ways I can think of to do this would either be to loop over the List<> 3 times, eachtime checking on a seperate accessor, writing it out to a StringBuilder, then returning StringBuilder.ToString() from the function.
Or, you could use 3 stringbuilders to hold each accessor type, then push all 3 from the function on return.

Categories