【问题标题】:How to use StreamReader and read from certain parts of the text file?如何使用 StreamReader 并从文本文件的某些部分读取?
【发布时间】:2020-11-09 17:51:14
【问题描述】:

我正在尝试创建一个已完成注册的登录系统。我想在你按下登录按钮的地方设置它,然后它使用 StreamReader 读取已使用我的注册表单更新的文件。问题是我已经构建了文件的编写方式(例如:当用户输入他们的详细信息时,它会将他们的详细信息保存在一个结构中,例如 Username: johndoe Password: Password1 using StreamWriter 在我的注册表单中)。但是当我使用 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();
            }
        }           

登录表单代码:

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

【问题讨论】:

    标签: c# visual-studio winforms


    【解决方案1】:

    是否要通过遍历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");
        }
    }
    

    【讨论】:

      【解决方案2】:

      您可以尝试在 StreamReader 中使用ReadLine() 方法,如下所示,您应该注意第二行包含用户名,第三行包含密码。此外,您应该使用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 直接从文件中读取,因此您必须执行字符串操作操作。相反,您也可以考虑使用其他方法(例如缓存或数据库操作)来跨页面/窗口持久保存值。

      【讨论】:

        【解决方案3】:

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

        在您的情况下:您不应将与操作员的通信与输入数据的序列化方式混为一谈。

        如果将 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 或 ComboBox 中,那么您将只需要在一个地方实现更改。

        序列化用户

        我的建议是坚持使用常见格式,例如 CSV、XML、JSon。这将使您的生活变得更加轻松,因为您可以为此使用 NUGET 包。

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

        public void Serialize(User user, string fileName)
        {
             using (var textWriter = System.IO.File.CreateText(fileName, ...))
             {
                  ... // see your own code
             }
        }
        

        顺便说一句:为什么写`\n,为什么不用TextWriter.WriteLine

        反序列化用户

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

        • 空行

        • 一行带有“UserId:” + Id

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

        • 一行“密码:”+密码

          公共用户反序列化(字符串文件名) { 使用 (var textReader = System.IO.File.OpenText(fileName, ...)) { string 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 格式更好。

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

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

        sw.Write("\n" + "用户ID:" + 用户ID + "\n"); sw.Write("用户名:" + txtUsername.Text + "\n"); sw.Write("密码:" + txtPassword.Text + "\n"); sw.Write("------------------------------------");

        【讨论】:

          猜你喜欢
          • 2014-04-28
          • 2021-10-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-12-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多