C# 如何使用StreamReader并读取文本文件的某些部分?

C# 如何使用StreamReader并读取文本文件的某些部分?,c#,visual-studio,winforms,C#,Visual Studio,Winforms,我正在尝试建立一个已经完成注册的登录系统。我想让它成为您按下登录按钮,然后它使用StreamReader读取已使用我的注册表单更新的文件的地方。问题是,我已经构建了文件的编写方式(例如:当用户输入其详细信息时,它会在我的注册表单中使用StreamWriter将其详细信息保存在类似Username:johndoe Password:Password1的结构中)。但当我使用StreamReader时,它会读取整行内容,而不是某些部分。如何只读取文本文件的某些部分 注册表格代码: bo

我正在尝试建立一个已经完成注册的登录系统。我想让它成为您按下登录按钮,然后它使用StreamReader读取已使用我的注册表单更新的文件的地方。问题是,我已经构建了文件的编写方式(例如:当用户输入其详细信息时,它会在我的注册表单中使用StreamWriter将其详细信息保存在类似Username:johndoe Password:Password1的结构中)。但当我使用StreamReader时,它会读取整行内容,而不是某些部分。如何只读取文本文件的某些部分

注册表格代码:

        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();
            }
        }           

您可以尝试在StreamReader中使用
ReadLine()
方法,如下图所示,您应该跟踪第2行包含用户名,第3行包含密码。此外,您应该使用
Regex
拆分并从文本中获取值,或者可以使用
分隔符并在比较之前修剪值

using (StreamReader sr = new StreamReader(path))
{
    int counter = 0;
    while (sr.Peek() >= 0)
    {
        string content = sr.ReadLine();
        var userName = content.split(':')[1].trim()...
        counter++;
    }
}
注意:
StreamReader直接读取文件,因此您必须执行字符串操作。相反,您还可以考虑其他方法,如缓存或数据库操作,以跨页/窗口持久化值。

< P>是否要检查用户是否已经通过遍历TXT文件输入了正确的用户名和密码? 测试信息结构:

UserID: 016696
Username: kkk
Password: p123
这里有一个简单的例子,你可以参考

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");
    }
}

您的代码有问题,因为您想在一个方法中做太多的事情。你应该

在您的情况下:您不应该将与运算符的通信与输入数据序列化的方式混为一谈

如果将HMI与保存和读取输入的方式分开:

  • 更易于重用:如果需要读取保存的登录数据,无需再次向操作员询问:只需读取即可
  • 更容易更改:如果您想要不同的HMI,您仍然可以以原始格式保存。如果您想以加密格式保存,您的HMI不需要更改
  • 易于测试:您可以通过单元测试保存/读取文件,而无需操作员输入
因此,将HMI与保存/读取文件分开,与处理登录数据分开。这将需要一些额外的输入,但您的代码将更容易理解,更容易测试,并且将来的更改对现有代码的影响将更小

为此,您需要一个包含登录数据的类。大概是这样的:

class User
{
    public int Id {get; set;}
    public string Name {get; set;}
    public string Password {get; set;}
}
用户人机界面 在HMI中显示用户,或从HMI获取用户。在你的课堂上:

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,
}
考虑创建一个属性:

public User User
{
    get => new User {Id = this.CreateUserId(), ...},
    set
    {
        this.txtUserName.Text = value.Name,
        ...
    }
}
如果将来您希望以不同的方式显示用户,比如在DataGridView或组合框中,那么只有一个地方需要实现更改

序列化用户 我的建议是坚持使用常见格式,如CSV、XML、JSon。这将使您的生活更加轻松,因为您可以使用NUGET软件包来实现这一点

如果您无法说服项目负责人这将是一个更好的解决方案,而他仍然希望您使用自己发明的格式,那么您需要两种方法:

public void Serialize(User user, string fileName)
{
     using (var textWriter = System.IO.File.CreateText(fileName, ...))
     {
          ... // see your own code
     }
}
顺便问一下:你为什么写“`\n,为什么不使用

反序列化用户

当前,您的文件具有以下布局:

  • 空行

  • 带有“UserId:+Id”的行

  • 带有“用户名:”+名称的行

  • 带有“Password:+Password”的行

    公共用户反序列化(字符串文件名) { 使用(var textReader=System.IO.File.OpenText(文件名,…) { 字符串emptyLine=textReader.ReadLine(); if(emptyLine==null) …//TODO:决定如果文件格式无效该怎么办。异常

         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,
         }
     }
    
    }

您是否看到,如果将来更改保存用户的格式,您只需要更改两种序列化方法?例如,如果您认为需要在文件中加密密码,或者如果您认为JSON格式更好

由于关注点的分离,您还可以在不需要显示的情况下使用用户序列化

关注点的分离也使单元测试更容易 } }

sw.Write(“\n”+”UserID:“+UserID+”\n”); sw.Write(“用户名:”+txtUsername.Text+“\n”); sw.Write(“密码:+txtPassword.Text+”\n”); sw.Write(“---------------------------------------”

     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,
     }
 }