Reputation: 21
I'm trying to make a login system with the sign-up already completed. I want to make it where you press the login button then it uses StreamReader to read the file that has been updated with my sign-up form. The problem is that I have structured how the file is written (example: when a user enters their details it saves their details in a structure like Username: johndoe Password: Password1 using StreamWriter in my sign-up form). But when I use StreamReader it reads the whole line instead of some parts. How would I read only some parts of my text file?
Code for sign-up form:
bool valid = true;
List<string> errorMessages = new List<string>();
if (string.IsNullOrWhiteSpace(txtUsername.Text))
{
valid = false;
errorMessages.Add("Username cannot be left empty.");
}
if (string.IsNullOrWhiteSpace(txtPassword.Text))
{
valid = false;
errorMessages.Add("Please enter a password.");
}
if (txtConfirmPass.Text != txtPassword.Text)
{
valid = false;
errorMessages.Add("Passwords do not match. Please enter matching passwords.");
}
if (valid == false)
{
string message = string.Join(Environment.NewLine, errorMessages);
MessageBox.Show(message, "Validate Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else if(valid == true)
{
Random rnd = new Random();
string userID = rnd.Next(0, 100000).ToString("D6");
//Use StreamWriter class.
using (StreamWriter sw = new StreamWriter("Test.txt", true))
{
sw.Write("\n" + "UserID: " + userID + "\n");
sw.Write("Username: " + txtUsername.Text + "\n");
sw.Write("Password: " + txtPassword.Text + "\n");
sw.Write("------------------------------------");
MessageBox.Show("Details have been saved");
frmLogin login = new frmLogin();
login.Show();
this.Hide();
}
}
Code for login form:
private void btnLogin_Click(object sender, EventArgs e)
{
bool valid = false;
using (StreamReader sr = new StreamReader("Test.txt"))
{
if(txtboxUser.Text == sr.ReadToEnd() && txtboxPass.Text == sr.ReadToEnd())
{
valid = true;
MessageBox.Show("Granted.");
}
else if(valid == false)
{
MessageBox.Show("Error");
}
}
}
Upvotes: 0
Views: 2445
Reputation: 30512
You have problems with your code, because you want to do too much in one method. You should separate your concerns
In your case: you shouldn't mix the communication with operator with the way that the input data is serialized.
If you separate the HMI from the way you save and read the input:
So separate the HMI from saving / reading the file, separate from processing the login data. This will take some extra typing, but your code will be much easier to understand, much easier to test, and future changes will have less impact on existing code.
For this you'll need a class that contains the login data. Something like this:
class User
{
public int Id {get; set;}
public string Name {get; set;}
public string Password {get; set;}
}
To display a User in the HMI, or to fetch it from the HMI. In your form class:
public User ReadUser()
{
return new User
{
Id = this.CreateUserId(),
Name = this.txtUserName.Text,
Password = this.txtPassword.Text,
};
}
// not sure if you need this:
public void DisplayUser(User user)
{
this.txtUserName.Text = user.Name,
this.txtPassword.Text = user.Password,
}
Consider to create a property:
public User User
{
get => new User {Id = this.CreateUserId(), ...},
set
{
this.txtUserName.Text = value.Name,
...
}
}
If in future you want to display users differently, like in a DataGridView, or a ComboBox, there will be only once place where you'll have to implement the changes.
My advice would be to stick to common formats, like CSV, XML, JSon. This would make life so much easier for you, because you can use NUGET packages for this.
If you can't convince your project leader that this would be a better solution and he still wants you to user you home invented format, you need two methods:
public void Serialize(User user, string fileName)
{
using (var textWriter = System.IO.File.CreateText(fileName, ...))
{
... // see your own code
}
}
By the way: why do you write `\n, why don't you use TextWriter.WriteLine
Deserialize a User
Currently your file has the following layout:
empty line
A line with "UserId: " + Id
A line with "UserName: " + Name
A line with "Password: " + Password
public User Deserialize(string fileName) { using (var textReader = System.IO.File.OpenText(fileName, ...)) { string emptyLine = textReader.ReadLine(); if (emptyLine == null) ... // TODO: decide what to do if invalid file format. Exception?
string txtId = textReader.ReadLine();
if (txtId == null)
... // TODO: decide what to do if invalid file format
int id = Int32.Parse(txtId);
// TODO: handle invalid txtId format. Exception?
string name = textReader.ReadLine();
string password = textReader.ReadLine();
// TODO: handle invalid name / password;
return new User
{
Id = id,
Name = name,
Password = password,
}
}
}
Do you see, that if in future you change the format in which Users are saved, that you only need to change the two serialization methods? for instance, if you think that passwords need to be encrypted in the file, or if you think that JSON format is better.
Because of the separation of concerns, you can use the serialization of Users also in case you don't need to display it.
The separation of concerns also makes unit test easier } }
sw.Write("\n" + "UserID: " + userID + "\n"); sw.Write("Username: " + txtUsername.Text + "\n"); sw.Write("Password: " + txtPassword.Text + "\n"); sw.Write("------------------------------------");
Upvotes: 0
Reputation: 3767
Do you want to check if the user has entered the correct username and password by traversing the txt file?
Test info structure:
UserID: 016696
Username: kkk
Password: p123
Here is a simple you can refer to.
private void btnLogin_Click(object sender, EventArgs e)
{
string username = "";
string password = "";
bool ischecked = false; // check if the Username line is read
bool loginsuccess = false; // check if succeed
string line;
// Read the file and display it line by line.
System.IO.StreamReader file = new System.IO.StreamReader("Test.txt");
while ((line = file.ReadLine()) != null)
{
// if true, this is password line
if (ischecked)
{
// get password
// call Trim to remove extra spaces
password = line.Split(':')[1].Trim();
// check username and password
if (txtUsername.Text == username && txtPassword.Text == password)
{
MessageBox.Show("Login Successfully");
loginsuccess = true; // login in success
break;
}
// reset boolean
ischecked = false;
}
// read Username line, next line is the corresponding password
if (line.Split(':')[0] == "Username")
{
username = line.Split(':')[1].Trim();
// set boolean to true
ischecked = true;
}
}
file.Close();
// login failed
if(!loginsuccess)
{
MessageBox.Show("Wrong username or password");
}
}
Upvotes: 1
Reputation: 7844
You can try to use ReadLine()
method in StreamReader like the one shown below, you should keep track that 2nd line contains the user name and the 3rd line contains the password. Also, you should use a Regex
to split and get the values from the text or you could use a :
delimiter and trim the values before comparison.
using (StreamReader sr = new StreamReader(path))
{
int counter = 0;
while (sr.Peek() >= 0)
{
string content = sr.ReadLine();
var userName = content.split(':')[1].trim()...
counter++;
}
}
Note: The StreamReader directly reads from the file, so you have to perform string manipulation operations. Instead, you might also consider other approaches like a cache or a database operation to persist the values across pages / windows.
Upvotes: 0