try catch validation for txt file - c#

I have a txt file named fileA.txt that I am trying to validate.
here is an example for fileA.txt
123, joshua, employee
134, vernon, manager
382, lisa, HR
So, what I am trying to do is read the contents of fileA and if e.g the value of the first index of the file is suppose to be the employee ID(an int) but has a string. I want to skip that line and go to the next using try catch. However, if everything is fine, I will return its value and add it to a new list. Any ideas on how may I do the validation part?
here is what I have for now to read the file and add it to a new list
public static List<Employee> readlist(string path)
{
var employees = new List<Employee>();
var content = File.ReadAllText(path);
var lines = content.Split('\n');
foreach (var line in lines)
{
var info = line.Split(',');
employees.Add(new Employee
(
int.Parse(info[0]),
info[1],
info[2]
));
}
return employees;
}
Hope what I have provided is sufficient, thank you for all the help in advance!

There is not need of using a try catch, you can simply use Int32.TryParse method to see if the expected value is a number, if is not a number then you just continue checking the other lines.
foreach (var line in lines)
{
var info = line.Split(',');
var isIdValid = Int32.TryParse(info[0], out int employeeId);
if(!isIdValid)
{
Console.WriteLine($"'{info[0]}' could not be parsed as an Int32.");
continue;
}
employees.Add(new Employee
(
employeeId,
info[1],
info[2]
));
}

Related

C# - check which element in a csv is not in an other csv and then write the elements to another csv

My task is to check which of the elements of a column in one csv are not included in the elements of a column in the other csv. There is a country column in both csv and the task is to check which countries are not in the secong csv but are in the first csv.
I guess I have to solve it with Lists after I read the strings from the two csv. But I dont know how to check which items in the first list are not in the other list and then put it to a third list.
There are many way to achieve this, for many real world CSV applications it is helpful to read the CSV input into a typed in-memory store there are standard libraries that can assist with this like CsvHelper as explained in this canonical post: Parsing CSV files in C#, with header
However for this simple requirement we only need to parse the values for Country form the master list, in this case the second csv. We don't need to manage, validate or parse any of the other fields in the CSVs
Build a list of unique Country values from the second csv
Iterate the first csv
Get the Country value
Check against the list of countries from the second csv
Write to the third csv if the country was not found
You can test the following code on .NET Fiddle
NOTE: this code uses StringWriter and StringReader as their interfaces are the same as the file reader and writers in the System.IO namespace. but we can remove the complexity associated with file access for this simple requirement
string inputcsv = #"Id,Field1,Field2,Country,Field3
1,one,two,Australia,three
2,one,two,New Zealand,three
3,one,two,Indonesia,three
4,one,two,China,three
5,one,two,Japan,three";
string masterCsv = #"Field1,Country,Field2
one,Indonesia,...
one,China,...
one,Japan,...";
string errorCsv = "";
// For all in inputCsv where the country value is not listed in the masterCsv
// Write to errorCsv
// Step 1: Build a list of unique Country values
bool csvHasHeader = true;
int countryIndexInMaster = 1;
char delimiter = ',';
List<string> countries = new List<string>();
using (var masterReader = new System.IO.StringReader(masterCsv))
{
string line = null;
if (csvHasHeader)
{
line = masterReader.ReadLine();
// an example of how to find the column index from first principals
if(line != null)
countryIndexInMaster = line.Split(delimiter).ToList().FindIndex(x => x.Trim('"').Equals("Country", StringComparison.OrdinalIgnoreCase));
}
while ((line = masterReader.ReadLine()) != null)
{
string country = line.Split(delimiter)[countryIndexInMaster].Trim('"');
if (!countries.Contains(country))
countries.Add(country);
}
}
// Read the input CSV, if the country is not in the master list "countries", write it to the errorCsv
int countryIndexInInput = 3;
csvHasHeader = true;
var outputStringBuilder = new System.Text.StringBuilder();
using (var outputWriter = new System.IO.StringWriter(outputStringBuilder))
using (var inputReader = new System.IO.StringReader(inputcsv))
{
string line = null;
if (csvHasHeader)
{
line = inputReader.ReadLine();
if (line != null)
{
countryIndexInInput = line.Split(delimiter).ToList().FindIndex(x => x.Trim('"').Equals("Country", StringComparison.OrdinalIgnoreCase));
outputWriter.WriteLine(line);
}
}
while ((line = inputReader.ReadLine()) != null)
{
string country = line.Split(delimiter)[countryIndexInInput].Trim('"');
if(!countries.Contains(country))
{
outputWriter.WriteLine(line);
}
}
outputWriter.Flush();
errorCsv = outputWriter.ToString();
}
// dump output to the console
Console.WriteLine(errorCsv);
Since you write about solving it with lists, I assume you can load those values from the CSV to the lists, so let's start with:
List<string> countriesIn1st = LoadDataFrom1stCsv();
List<string> countriesIn2nd = LoadDataFrom2ndCsv();
Then you can easily solve it with linq:
List<string> countriesNotIn2nd = countriesIn1st.Where(country => !countriesIn2nd.Contains(country)).ToList();
Now you have your third list with countries that are in first, but not in the second list. You can save it.

Reading tab file in C# and appending to a string

I am reading a tab-delimited file in C#. I am passing each value to an INSERT statement by using StringBuilder.
My code is as follows:
StringBuilder sCommand = new StringBuilder("INSERT INTO `interactions` (`id`, `id_interaction`) VALUES ");
var lines = File.ReadLines(#"C:\Users\Edges.txt").Take(50).ToArray().Distinct();
List<string> rows = new List<string>();
foreach (var item in lines)
{
string[] values = item.Split("\t");
rows.Add(string.Format("('{0}', '{1}')", MySqlHelper.EscapeString(values[0]), MySqlHelper.EscapeString(values[1])));
var sep = string.Join(",", rows);
sCommand.Append(sep);
}
On breakpoint mode, I have sep holding values: "('6416', '2318'),('84665', '88'),('90', '2339'),('2624', '5371'),('6118', '6774')" which are good.
But when I append the sep to the sCommand string, the string turns out to be: INSERT INTOinteractions(id,id_interaction) VALUES ('6416', '2318')('6416', '2318'),('84665', '88')('6416', '2318'),('84665', '88'),('90', '2339')('6416', '2318'),('84665', '88'),('90', '2339')...
Can someone please help me with this ? Why does this ('6416', '2318')('6416', '2318'),('84665', '88') sequence occur?
To understand the behavior, it would be worth examining the values of rows,sep, and sCommand in each iteration.
Iteration 1
Iteration 2
Iteration 3
Iteration 4
As you can observe, in each iteration, you are adding an string to the List.However, when you append to sCommand, you are joining the entire list and appending. This would mean, you are appending strings, which are already part of the sCommand repeatedly with each iteration.
To fix it, you would need to make following moving following lines out of the loop.
var sep = string.Join(",", rows);
sCommand.Append(sep);
What this does is that you would first create the complete collection within the loop and then, only at the end, appends the values to the StringBuilder.
Code would look like
foreach (var item in lines)
{
string[] values = item.Split('\t');
rows.Add(string.Format("('{0}', '{1}')", values[0], values[1]));
}
var sep = string.Join(",", rows);
sCommand.Append(sep);
sCommand.Append(sep) is being called for each iteration of the foreach loop.
Your string is likely being correctly appended to the end, and the snippet you showed (VALUES ('6416', '2318')('6416', '2318'),('84665', '88')('6416', '2318'),('84665', '88'),('90', '2339')('6416', '2318'),('84665', '88'),('90', '2339')...) may be the result of appending done in earlier iterations of the foreach loop.
It might be worth inspecting your Edges.txt file?
Edit: Anu has the correct explanation - see Anu's answer.
I created a little class for the values in the file. I separated reading the file from the database code; just returning a list to the database code.
class Values
{
public string Val1 { get; set; }
public string Val2 { get; set; }
public Values(string v1,string v2)
{
Val1 = v1;
Val2 = v2;
}
}
private void InsertValues()
{
List<Values> lst = ReadFile();
using (MySqlConnection cn = new MySqlConnection("Your connection string"))
using (MySqlCommand cmd = new MySqlCommand("INSERT INTO `interactions` (`id`, `id_interaction`) VALUES (#id, #idInteraction);", cn))
{
cmd.Parameters.Add("#id", MySqlDbType.VarChar, 50);
cmd.Parameters.Add("#idInteraction", MySqlDbType.VarChar, 100);
cn.Open();
foreach (Values v in lst)
{
cmd.Parameters["#id"].Value = v.Val1;
cmd.Parameters["#idInteraction"].Value = v.Val2;
cmd.ExecuteNonQuery();
}
}
}
private List<Values> ReadFile()
{
List<Values> lst = new List<Values>();
string[] lines = File.ReadAllLines(#"C:\Users\Edges.txt");
foreach (string line in lines)
{
string[] vals = line.Split(',');
Values v = new Values(vals[0].Trim(), vals[1].Trim());
lst.Add(v);
}
return lst;
}
Move the last two lines out of the foreach loop.
rows.Add is in your foreach loop, this adds as many rows as there are lines. So this is the full set you need.
Your append is in the foreach so it is appending the rows it has at that moment:
e.g. I have the numbers 1,2,3,4,5,6,7, in the foreach I add it as rows, the first time I will have 1, this is appended, my string contains "1".
The second time around we will have 1 & 2, this is appended. my string contains now 1,1,2 (1 from last time and 1,2 now.
the third time the rows have 1,2 & 3. the string is 1,1,2,1,2,3
The var sep is adding in

C# Read and split from .txt to struct array

I'm trying to make a basic login for my console app. I store the user data in a .txt file like this:
ID;Name;IsAdmin. The txt has several lines.
In the app I want to store user data in a struct User array. I can't seem to find the method to read the file, split and put the different data to the right place. This is what I have so far:
Loading user data to struct array
public static void LoadIDs()
{
int entries = FileHandling.CountRows(usersPath);
User[] users = new User[entries]; //Length depends on how many lines are in the .txt
for (int i = 0; i < users.Length; i++)
{
users[i] = new User(1,"a",false); //ID(int), name, isAdmin [This is where I want to put the data from the .txt]
}
}
Reading and spliting the text
public static string ReadFileToArray(string path)
{
String input = File.ReadAllText(path);
foreach (var record in input.Split('\n'))
{
foreach (var data in record.Split(';'))
{
return data;
}
}
return null;
}
I know that this doesn't work at all this way but my knowledge is limited yet and I cannot think of other solutions.
You have a better tool to store your users. Instead of an array (that forces you to know the length of the data loaded) you can use a List where you can add your elements while you read them.
Another point to change is the File.ReadAllText in File.ReadLines. This will allow to read line by line your file directly in the loop
public List<User> BuildUserList(string path)
{
List<User> result = new List<User>();
foreach (var record in File.ReadLines(path)
{
string[] data = record.Split(';'))
User current = new User();
current.ID = Convert.ToInt32(data[0]);
current.Name = data[1];
current.IsAdmin = Convert.ToBoolean(data[2]);
result.Add(current);
}
return result;
}
Now you can use the list like an array if you need
List<User> users = BuildUserList("yourfile.txt");
if(users.Count > 0)
{
Console.WriteLine("Name=" + users[0].Name);
}
If I were to assume your file especially each line having Id;Name;Admin values, I would write something like below to extract it. Please note that there are simple syntax out there but following logic will be helpful for beginners to understand how this could be achieved.
List<User> userList = new List<User>();
// Read the file located at c:\test.txt (this might be different in your case)
System.IO.StreamReader file = new System.IO.StreamReader(#"c:\test.txt");
string line;
while ((line = file.ReadLine()) != null)
{
//following logic will read each line and split by the separator before
// creating a new User instance. Remember to add more defensive logic to
// cover all cases
var extract = line.Split(';');
userList.Add(new User()
{
Id = extract[0],
Name = extract[1],
IsAdmin = extract[2]
});
}
file.Close();
//at this stage you will have List of User and converting it to array using following call
var userArray = userList.ToArray();
And just as another variant, a linq-solution could look like this:
var users = (
from string line in System.IO.File.ReadAllLines(#"..filepath..")
let parts = line.Split(';')
where parts.Length == 3
select new User() {
ID = Convert.ToInt32(parts[0]),
Name = parts[1],
IsAdmin = Convert.ToBoolean(parts[2])}
).ToArray();
This can be elegant and short, error handling may be a bit more difficult.
This will read your file lazily, so it can handle extremely huge files with ease (assuming the rest of your code can):
public IEnumerable<User> ReadUsers(string path)
{
return File.ReadLines(path)
.Select(l=>l.Split(';'))
.Select(l=> new User
{
Id = int.Parse(l[0]),
Name = l[1],
IsAdmin = bool.Parse(l[2])
});
}
or
public IEnumerable<User> ReadUsers(string path)
{
return File.ReadLines(path)
.Select(l=>l.Split(';'))
.Select(l=> new User(int.Parse(l[0]), l[1], bool.Parse(l[2])));
}

C# Reading From A File

I have an assignment to read text in from a text file. The text is an inventory with department names followed by the quantity of items in the department and then items underneath the separate departments with the item name, quantity, and price. A part of the text file is shown here:
Stationary, 4
Notebook, 20, .99
Pens, 50, .50
Pencils, 25, 0.09
Post It Notes, 30, 4.99
Tools, 6
Band Saw, 3, 299.99
Cresent Wrench, 12, 8.49
Circular Saw, 5, 89.99
Tile Cutter, 2, 149.99
Screwdriver, 70, 2.99
Measuring Tape, 34, 10.99
I'm able to load the text file in just fine. My task is to take in user input for them decide which department they want to shop on. How am I able to display just the departments and then just the items of the desired department from the user? I have a method to output all of the departments and items shown below. This is my first time working with text files with C# so I have no idea what I am doing.
static void ReadDepartments(out List<Dept> s)
{
string line; // detail line read from file
string[] tokens; // break line up into tokens
string deptName; // name of department
int deptQuan; // quan of different items in dept
s = new List<Dept>();
try
{
using (StreamReader sr = new StreamReader(#"..\..\inventory.txt"))
{
while (sr.Peek() >=0)
{
List<Item> myItemList = new List<Item>(); // new instance of tmp List
line = sr.ReadLine();
tokens = line.Split(',');
deptName = tokens[0];
deptQuan = Convert.ToInt32(tokens[1]);
for (int i=0; i< deptQuan; i++)
{
// read each line of dept and build a list of items
line = sr.ReadLine();
tokens = line.Split(',');
Item myItem = new Item(tokens[0], Convert.ToInt32(tokens[1]), Convert.ToDouble(tokens[2]));
myItemList.Add(myItem);
}
s.Add(new Dept(deptName,deptQuan, myItemList));
}
}
}
catch (Exception e)
{
Console.WriteLine("Can't open file because {0}", e.Message);
}
}
static void PrintInventory(List<Dept> s)
{
foreach (Dept d in s)
{
Console.WriteLine("Dept: {0,-20} [{1} items]", d.Name, d.NumItems);
for (int i = 0; i < d.NumItems; i++)
Console.WriteLine(" {0,-15} {1,4} {2,7:$,##0.00}", d.GetItem(i).Name,
d.GetItem(i).Quan, d.GetItem(i).PriceEach);
}
}
I started a method to check if the desired department is a valid department shown below. Is there an easier way to implement the valid[] variable instead of including all of the department names? I will have to error check for valid items and that seems like it would be very tedious.
static string GetDepartment(string prompt)
{
string[] valid = {"BOOKS", "FOOD", "VIDEO", "SPORTS", "STATIONARY", "TOOLS"};
string ans = GetString(prompt, valid, "Inavlid response. Please choose a department.");
return ans;
}
static string GetString(string prompt, string[] valid, string error)
{
string response;
bool OK = false;
do
{
Console.Write(prompt);
response = Console.ReadLine().ToUpper();
foreach (string s in valid) if (response == s) OK = true;
if (!OK) Console.WriteLine(error);
}
while (!OK);
return response;
}
Your method that reads from the text file results in you having a List<Dept>. So you can generate a list of valid department names by going through the list of departments that you have read from the text file.
LINQ is great for searching through data and checking if items exists and what not.
Since you have all of your departments in a List you can query it via some different methods. Either search your raw data directly
using System.Linq;
...
List<Dept> departments;
...
departments.Any(dept => dept.Name == response);
Or if you want to send the names to your GetString method:
GetString(prompt, departments.Select(dept => dept.Name), ...);
...
string GetString(string prompt, IEnumerable<string> valids, string error)
...
valids.Any(valid => valid == response);
If you want to use the Department instead you can use FirstOrDefault instead (which also takes a predicate) and check for null if the item does not exist
Department found = departments.FirstOrDefault(dept => dept.Name == response);
if (found == null) //department name does not exist
If everything is ok on your code then you can add a if statement to check whether if it is your desired department info to print. I didn't check the whole code. You also can solve this problem with Linq (it will be more smart coding then) but your code seems to me as a starter code, so may be it will be a little inefficient but I hope it will solve your problem.
static void PrintInventory(List<Dept> s,string userInputDepartmentName)
{
if(s == null && s.Count <= 0)
return;
foreach (Dept d in s)
{
if(d.Name.Equals(userInputDepartmentName))
{
Console.WriteLine("Dept: {0,-20} [{1} items]", d.Name, d.NumItems);
for (int i = 0; i < d.NumItems; i++)
Console.WriteLine("{0,-15} {1,4} {2,7:$,##0.00}", d.GetItem(i).Name,d.GetItem(i).Quan, d.GetItem(i).PriceEach);
}
}
}

Generating IDs using StreamReader (Last Line)

I'm trying to generate Item IDs using StreamReader on my .CSV file (It has to be a .csv file). The Item ID should start at 1000 and go up (1001, 1002, etc.)
Right now, if the user presses "Generate ID", it will search the entire file for the value "1000", if it doesn't exist, it will write "1000" in the textbox.
Here's what I need help with: If the file contains "1000", I want it to read the LAST line, increase it by 1, then write the value in the textbox.. So, if my last value is 1005 in the .csv file, I want it to write 1006 in the textbox.
private void GenerateID_Click(object sender, EventArgs e)
{
try
{
string searchString = "1000";
using (StreamReader sr = new StreamReader("file.csv"))
{
string line;
while ((line = sr.ReadLine()) != null)
{
if (line.Contains(searchString))
{
/* If file contains 1000, read the LAST line
* (Whatever number that may be: 1001, 1002, 1003, etc.)
* and increase that number by 1, then write to textbox. */
}
else
{
invItemIDField.Text = Convert.ToString("1000");
}
}
}
}
catch (Exception)
{
MessageBox.Show("The file could not be read");
}
}
I suggest you use FileHelpers. It's the most suitable library for reading CSV files.
To install this, you need to install first NuGet. Once installed, go to Tools > Library Package Manager > Package Manager Console:
Then, type in: Install-Package Filehelpers
You're good to go!
Import FileHelpers to your code
using FileHelpers;
Create a class that describes the structure of your CSV:
DelimitedRecord("'")]
public class MyCsv
{
public int Column1; // Your ID column
public string SomeOtherColumn;
}
Create a List<MyCsv>:
List<MyCsv> myList;
Then, to load your CSV:
FileHelperEngine<MyCsv> engine = new FileHelperEngine<MyCsv>();
myList = new List<MyCsv>(engine.ReadFile("my.csv")); // replace with your filename or variable containing the filename
You can now read your CSV by accessing the list myList:
foreach(MyCsv line in myList) {
// Do something;
}
Each object inside that list corresponds to each row in your CSV. In order to access the first column of a row (given the foreach loop above):
line.Column1
So, if you need to compare values, you can either use LINQ or do the traditional loop-search:
foreach(MyCsv line in myList) {
if (txtId.Text == line.Column1.ToString()) {
found = true;
break;
}
}
Then, to get the id of the last row:
myList.[myList.Count - 1].Column1
You can do the rest. Cheers!
Here's my go at it, it's slighlty different from yours, but it works. Granted there are things you must consider, such as are the elements surrounded in quotes, are the line breaks \r\n, and the like:
int TextBoxValue = 1000;
var reader = new StreamReader(File.OpenRead(#"C:\Users\J\Desktop\New Text Document (4).txt"));
var contents = reader.ReadToEnd().Split(new string[] {"\r\n"}, StringSplitOptions.None);
var iValueExists = (from String sLine in contents
where sLine.Contains("1000")
select sLine).Count();
if (iValueExists > 0)
{
TextBoxValue = int.Parse(contents.Last().Split(new string[] {","}, StringSplitOptions.None).First()) + 1;
}
invItemIDField.Text = TextBoxValue;
reader.Close();

Categories