Looping through a list and checking for contents? - c#

I'm trying to set up a simple command for entering all callers into a .txt file, everything is working out nicely except for the part where I want to loop through the list and check that no one is trying to enter more than once, it's not causing any problems to the application itself but it's not doing what it's supposed to do.
Any help would be much appreciated.
string filePath = Secret.Secrets.fileDestination;
var username = msg.Author.Username;
//ulong
var ID = msg.Author.Id;
string IDout = ID.ToString();
List<string> entries = File.ReadAllLines(filePath).ToList();
if(entries.Contains(IDout))
{
await msg.Channel.SendMessageAsync($"{msg.Author.Mention}, you have already been entered.");
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine($"Denied entry for {msg.Author.Mention}");
return;
}
entries.Add($"{username}, {ID}");
File.WriteAllLines(filePath, entries);
await msg.Channel.SendMessageAsync($"{msg.Author.Mention} has been entered!");

Related

c# - Method Failing to Parse CSV File

I am trying to work through a school assignment that has us use a C# program to parse data from a CSV file and add it to a table in a local database. When I try to run the program though, the method I am using fails to parse any of the data into the object.
Here is the method I am using:
//Parse CSV line
public bool ParseCSVline(string aLine)
{
try
{
string[] fields = aLine.Split(',');
this.Item_ID = int.Parse(fields[0]);
this.Invent_id = int.Parse(fields[1]);
this.Itemsize = fields[2];
this.Color = fields[3];
this.Curr_price = decimal.Parse(fields[4]);
this.Qoh = int.Parse(fields[5]);
return true; //if everything parsed, return true
}
catch (Exception ex)
{
Console.Write("Failed to Parse");
return false; //if a parse failed, return false
}
When running the program the method keeps throwing the Exception instead of actually parsing the data. For clarity, here is the section in the Main program that is calling everything:
/Step 2 - Open input file
//Set where the file comes from
string filepath = #"C:\Users\Karlore\Documents\School\SAI-430\";
string filename = #"NewInventory.csv";
//Open reader
StreamReader theFile = new StreamReader(filepath + filename);
//Step 3 - Create an object to use
Item theItem = new Item();
//Step 4 - Loop through file and add to database
while (theFile.Peek() >= 0)
{
//Get one line and parse it inside the object
theItem.ParseCSVline(filename);
//Check to see if item is already there
if (theItem.IsInDatabase(connection))
{
continue;
}
else
{
//Add the new item to the database if it wasn’t already there
theItem.AddRow(connection);
}
} //end of while loop
If anyone can point out where I may have made an error, or point me in the right direction I would appreciate it.
Replace the line:
theItem.ParseCSVline(filename);
by:
theItem.ParseCSVline(theFile.ReadLine());

Best way StreamReader skipping Null or WhiteSpace line

After searching and trying the different ways I found I either wasn't happy with the way I was doing the code or it didn't work right for me. I'm new at programming so my understanding is limited. Please keep in mind with the answer.
I want to read a .csv file line by line and skipping lines that are blank. With the contents of the lines I want to put into a list of object. I have everything working except for the skipping line part. Also any feedback about improving any parts of my code are all welcome. I like constructive criticism.
public void CardaxCsvFileReader()
{
string cardaxCsvPath = (#"C:\Cardax2WkbTest\Cardax\CardaxTable.csv");
try
{
using (System.IO.StreamReader cardaxSR =
new System.IO.StreamReader(System.IO.File.OpenRead(cardaxCsvPath)))
{
string line = "";
string[] value = line.Split(',');
while (!cardaxSR.EndOfStream)
{ // this commented out part is what I would like to work but doesn't seem to work.
line = cardaxSR.ReadLine();//.Skip(1).Where(item => !String.IsNullOrWhiteSpace(item));
value = line.Split(',');
if (line != ",,,,,") // using this as temp to skip the line because the above commented out part doesn't work.
{
CardaxDataObject cardaxCsvTest2 = new CardaxDataObject();
cardaxCsvTest2.EventID = Convert.ToInt32(value[0]);
cardaxCsvTest2.FTItemID = Convert.ToInt32(value[1]);
cardaxCsvTest2.PayrollNumber = Convert.ToInt32(value[2]);
cardaxCsvTest2.EventDateTime = Convert.ToDateTime(value[3]);
cardaxCsvTest2.CardholderFirstName = value[4];
cardaxCsvTest2.CardholderLastName = value[5];
Globals.CardaxQueryResult.Add(cardaxCsvTest2);
}
}
}
}
catch (Exception)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\"", cardaxCsvPath);
}
EDITED
If you are lines are not truly blank and contain commas, you can split with RemoveEmptyEntries option and then check the column count.
while (!cardaxSR.EndOfStream)
{ // this commented out part is what I would like to work but doesn't seem to work.
line = cardaxSR.ReadLine();//.Skip(1).Where(item => !String.IsNullOrWhiteSpace(item));
value = line.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries); // <-- Remove empty columns while splitting. It has a side-effect: Any record with just a single blank column will also get discarded by the if that follows.
if (value.length < 6)
continue;
CardaxDataObject cardaxCsvTest2 = new CardaxDataObject();
cardaxCsvTest2.EventID = Convert.ToInt32(value[0]);
cardaxCsvTest2.FTItemID = Convert.ToInt32(value[1]);
cardaxCsvTest2.PayrollNumber = Convert.ToInt32(value[2]);
cardaxCsvTest2.EventDateTime = Convert.ToDateTime(value[3]);
cardaxCsvTest2.CardholderFirstName = value[4];
cardaxCsvTest2.CardholderLastName = value[5];
Globals.CardaxQueryResult.Add(cardaxCsvTest2);
}
Another improvement feedback I have: When you catch an exception, it's a good practice to log the exception in addition to your custom error line. A custom error line might be good for say website users, but as a developer running some service you will appreciate the actual exception stack trace. It will help you debug a bug easier.
catch (Exception ex)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\".\r\n Exception: {1}", cardaxCsvPath, ex.ToString());
}
Just check if value.Length == 6, this way it'll skip lines which don't contain enough data for your columns
Use a dedicated CSV parser, such as the EasyCSV class available here*:
https://github.com/jcoehoorn/EasyCSV
public void CardaxCsvFileReader()
{
try
{
string cardaxCsvPath = (#"C:\Cardax2WkbTest\Cardax\CardaxTable.csv");
Globals.CardaxQueryResult =
EasyCSV.FromFile(cardaxCsvPath)
.Where(r => r.Any(c => !string.IsNullOrEmpty(c)))
.Select(r => CardaxDataObject() {
cardaxCsvTest2.EventID = int.Parse(r[0]),
cardaxCsvTest2.FTItemID = int.Parse(r[1]),
cardaxCsvTest2.PayrollNumber = int.Parse(r[2]),
cardaxCsvTest2.EventDateTime = DateTinme.Parse(r[3]),
cardaxCsvTest2.CardholderFirstName = r[4],
cardaxCsvTest2.CardholderLastName = r[5]
}).ToList();
}
catch (Exception)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\"", cardaxCsvPath);
}
}
I also recommend re-thinking how you structure this. The code below is better practice:
public IEnumerable<CardaxDataObject> ReadCardaxCsvFile(string filename)
{
//no try block at this level. Catch that in the method that calls this method
return EasyCSV.FromFile(cardaxCsvPath)
.Where(r => r.Any(c => !string.IsNullOrEmpty(c)))
// You may want to put a try/catch inside the `Select()` projection, though.
// It would allow you continue if you fail to parse an individual record
.Select(r => CardaxDataObject() {
cardaxCsvTest2.EventID = int.Parse(r[0]),
cardaxCsvTest2.FTItemID = int.Parse(r[1]),
cardaxCsvTest2.PayrollNumber = int.Parse(r[2]),
cardaxCsvTest2.EventDateTime = DateTinme.Parse(r[3]),
cardaxCsvTest2.CardholderFirstName = r[4],
cardaxCsvTest2.CardholderLastName = r[5]
});
}
Suddenly the method boils down to one statement (albeit a very long statement). Code like this is better, because it's more powerful, for three reasons: it's not limited to using just the one input file, it's not limited to only sending it's output to the one location, and it's not limited to only one way to handle errors. You'd call it like this:
try
{
string cardaxCsvPath = (#"C:\Cardax2WkbTest\Cardax\CardaxTable.csv");
Globals.CardaxQueryResult = ReadCardaxCsvFile(cardaxCsvPath).ToList();
}
catch (Exception)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\"", cardaxCsvPath);
}
or like this:
try
{
string cardaxCsvPath = (#"C:\Cardax2WkbTest\Cardax\CardaxTable.csv");
foreach (var result in ReadCardaxCsvFile(cardaxCsvPath))
{
Globals.CardaxQueryResult.Add(result);
}
}
catch (Exception)
{
myLog.Error("Unable to open/read Cardax simulated punch csv file! " +
"File already open or does not exist: \"{0}\"", cardaxCsvPath);
}
I also recommend against using a Globalsclass like this. Find a more meaningful object with which you can associate this data.
* Disclaimer: I am the author of that parser

Display a button based on a login comparison

I'm new to C# and I'm trying to implement a button.visible true/false based on the contents of a txt file. Everything I've written to date is unstable at best. This is for a Winform stand alone application in the main dialog box.
In an ideal world it seems it should be simpler. I want the code to open Permissions.txt, which I know I am successfully accessing as the MessageBox will show the first name in the list, and compare the Environment.UserName with all of the names in the .txt file. Once the button is displayed it opens a new dialog box.
Anyone willing to teach a newcomer. I've been searching for a while and I don't see it.
I have also tried working with File.Readlines with no success.
Thank you in advance for any assistance you're willing to provide.
Frank Pytel
public void hideWidget()
{
//gets the users login name from the system
string newName = userNameOnly();
// Read the file and display it line by line.
System.IO.StreamReader file =
new System.IO.StreamReader(dataFolder + "\\Permissions.txt");
//This next bit called Original Code works on my local when I access it, when accessed from a server, but not for other users.
//Original code
//while ((line = file.ReadLine()) != null)
//{
// if (line == newName)
// {
// WidgetForm.Visible = true;
// }
// else
// {
// WidgetForm.Visible = false;
// }
// //MessageBox.Show(line);
// counter++;
//}
//file.Close();
//This is where I am at currently. Again it's not picking up all of the names in the .txt file.
while (file.ReadLine() != null)
{
//string line;
string line = file.ReadLine();
if (newName == file.ReadLine())
{
WidgetForm.Visible = false;
}
else
{
WidgetForm.Visible = true;
}
int counter = 0;
//MessageBox.Show(line);
//MessageBox.Show(file.ReadLine());
counter ++;
}
//file.Close();
}
EDITED....
Also if there is anyone that could possibly explain how string line; is being set to my user name. That is how it should have been set, but I've never told it line == newName in the original code. I thought that is what the While is for. To check to see if they are equal..
FINAL EDIT.
Here is what I got to work. Thanks #Bedford.
This portion goes directly below the Form1 class
string[] lines = File.ReadAllLines(dataFolder + "\\Permissions.txt");
This is the logic behind the hideWidget() button
public void hideWidget()
{
//Make all userNames available to the logic
string newName = userNameOnly();
//variable to decide if userExists is true/false
bool userExists;
//Loop through all of the userNames in the file and see if it matches the userName login
while (lines != null)
{
//Decide to make the button available if userExists does exist in the file
if (lines != null)
{
userExists = lines.Any(ln => ln == newName);
WidgetForm.Visible = userExists;
}
//Do nothing if the userName does not match anyone in the Permissions.txt file. The button default Visible is false
else
{
}
return;
}
}
I'm posting this snippet so that others might benefit from it. Thanks again #Bedford. This NEWB really appreciates the assistance. HAGD!! :-)
You can read all the lines from a file with the File.ReadAllLines static method, and then use a LINQ query to check whether any of the lines match the user name:
string[] lines = File.ReadAllLines(Path.Combine(dataFolder, "Permissions.txt"));
bool userExists = lines.Any(ln => ln == newName); // or any comparison you like
// use the bool variable to set the visibility
WidgetForm.Visible = userExists;

Keep the text in textboxes saved

I have these textboxes where the user enters data and then presses a button to process the data. Now the data entered by the user is alot and to give the user some slack I want to make it possible whenever you press the button, the application saves the data, so when you close the application and start it back up again the textboxes are filled with the last entered data.
I was thinking about using a .txt file to save the data. Only I have found some difficulties with this. One of the problems is that I keep getting a messagebox from the microsoft .NET Framework everytime I try to run my application. The messagebox says the Index was outside the bounds of the array. Even though I think my code doesn't exceed the bounds of my array.
And here is the code that I use:
First I declared an array and filled it with variables that contain the content of the textboxes:
string[]settings = new string[5];
settings[0] = openKey;
settings[1] = secretKey;
settings[2] = statusRequestPath;
settings[3] = statusRequestAPI;
settings[4] = setSeconds.ToString();
Then I use the following code to write the data to a text file.
using (StreamWriter writeFile = new StreamWriter(#"C:\Audio Silence Detector\AudioSilenceDetector.txt"))
{
foreach (string line in settings)
{
writeFile.WriteLine(line);
}
}
And to put the text of the .txt file back in the application I have put this in the formload:
string[] lines = System.IO.File.ReadAllLines(#"C:\Audio Silence Detector\AudioSilenceDetector.txt");
tbOpenKey.Text = lines[0];
tbSecretKey.Text = lines[1];
tbStatusRequestPath.Text = lines[2];
tbStatusRequestAPI.Text = lines[3];
tbSeconds.Text = lines[4];
I changed my code to this and it seems to have fixed the issue I was having:
if (lines.LongLength == 5)
{
tbOpenKey.Text = lines[0];
tbSecretKey.Text = lines[1];
tbStatusRequestPath.Text = lines[2];
tbStatusRequestAPI.Text = lines[3];
tbSeconds.Text = lines[4];
}
The problem is in file loading.
string[] lines = System.IO.File.ReadAllLines(#"C:\Audio Silence Detector\AudioSilenceDetector.txt");
You can not be sure that lines now contains 5 elemetns. You probably should check for that.
if(lines.Length == 5)
{
tbOpenKey.Text = lines[0];
tbSecretKey.Text = lines[1];
tbStatusRequestPath.Text = lines[2];
tbStatusRequestAPI.Text = lines[3];
tbSeconds.Text = lines[4];
}
else
{
MessageBox.Show("Input Data is Wrong");
}

finding a line that contains… etc

Ok, well i'm basically trying to find a certain line withing "Users.txt"
Heres my code so far.
if (ok == "b" || ok == "B")
{
using (StreamWriter w = File.AppendText("Users.txt"))
{
//Test
Out.WriteLine("Please state the username");
string user = Console.ReadLine();
Out.WriteLine("Checking..");
if (w.Equals(user))
{
Out.WriteLine("Username is taken");
}
Thread.Sleep(pause);
Out.WriteLine("Please state the password for the user");
string pass = Console.ReadLine();
Logger(user, pass, w);
// Close the writer and underlying file.
w.Close();
Out.WriteLine("Checking..");
Out.WriteBlank();
Thread.Sleep(pause);
Out.WriteLine("Anything else Mr." + Environment.UserName + " ?");
}
string choice = Console.ReadLine();
if (choice == "no")
{
Boot();
}
if (choice == "yes")
{
Console.Clear();
Console.Title = "Administrator Panel";
Panel();
}
}
want it to see if the "user" is taken, then stop them from executing the process.
Thanks for the help.
Try reading (StreamReader with File.Open) each existing username into an array/List and then comparing user input against that list.
Your current code doesn't actually read anything since you're using a StreamWriter with File.AppendText which just lets you write to the end of a file.
Examples:
Reading File into a List
List<string> users = new List<string>();
using (StreamReader r = new StreamReader("Users.txt"))
{
string line;
while ((line = r.ReadLine()) != null)
{
users.Add(line);
}
}
...
string user = Console.ReadLine();
Out.WriteLine("Checking..");
if (users.Contains(user))
{
Out.WriteLine("Username is taken");
}
There are various problems with your code. Let's see if we can break it down one piece at a time.
using (StreamWriter w = File.AppendText("Users.txt"))
This code would be useful if you wanted to open "Users.txt" and append text to it. Since you want to open a file and read from it, you need to use a different object, the StreamReader object:
using (StreamReader r = File.Open("Users.txt"))
Next, you want to check if the given Username is in the file. You're doing:
if (w.Equals(user))
{
Out.WriteLine("Username is taken");
}
This isn't going to work. You are comparing a StreamWriter object with a String object. They will never be equal.
What you need to do instead is change the order of your program like this:
First, read the entire contents of the file into memory. Then, outside of the Using statement, process your user input and your username/password checking.
Let's assume the file is organized like this:
username,password
username2,password2
johnsmith,mysecretcode
janedoe,blahblah
You could, for example, read each line into a Dictionary object, where the Key is the username and the Value is the password.
Dictionary<String, String> myDictionary = new Dictionary<String, String>
// Example of adding ONE username/password to the dictionary
myDictionary.Add("username", "password");
Then, checking for the username would be as simple as
bool containsUsername = myDictionary.ContainsKey(username);
And checking the password would be:
bool doesPasswordMatch = myDictionary[username] == givenPassword;
Give it a shot! C# is a great language to learn.

Categories

Resources