简体   繁体   English

关于文件读取和比较的问题

[英]Question about file reading and comparing

First Off I have a File That Looks Like This:首先我有一个看起来像这样的文件:

//Manager Ids

ManagerName: FirstName_LastName
ManagerLoginId: 12345

And a Text Box That has a five digit code(ex. 12345) That gets entered.以及一个包含五位数代码(例如 12345)的文本框。 When the Enter Key Is pressed it is assigned to a String called: "EnteredEmployeeId", Then What I need is to search the Entire file above for "EnteredEmployeeId" and if it matches then it will open another page, if it doesn't find that number then display a message(That tells you no employee Id found).当按下 Enter 键时,它被分配给一个名为:“EnteredEmployeeId”的字符串,然后我需要在上面的整个文件中搜索“EnteredEmployeeId”,如果匹配则打开另一个页面,如果找不到该号码然后显示一条消息(告诉您未找到员工 ID)。

So essentially Im trying to open a file search the entire document for the Id then return true or false to allow it too either display an error or open a new page, and reset the EnteredEmployeeId to nothing.所以本质上,我试图打开一个文件,在整个文档中搜索 Id,然后返回 true 或 false 以允许它也显示错误或打开一个新页面,并将 EnteredEmployeeId 重置为空。

My Code So Far:到目前为止我的代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Rent_a_Car
{
    public partial class Employee_Login_Page : Form
    {
        public Employee_Login_Page()
        {
            InitializeComponent();
        }
        string ManagersPath = @"C:\Users\Name\Visual Studios Project Custom Files\Rent A Car Employee Id's\Managers\Manager_Ids.txt"; //Path To Manager Logins
        string EnteredEmployeeId;


        private void textBox1_TextChanged(object sender, EventArgs e)
        {

        }

        private void Employee_Id_TextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) &&           //Checks Characters entered are Numbers Only and allows them
                (e.KeyChar != '0'))
            {
                e.Handled = true;
            }
            else if (e.KeyChar == (char)13)                                         //Checks if The "Enter" Key is pressed
            {
                EnteredEmployeeId = Employee_Id_TextBox.Text;                         //Assigns EnteredEmployeeId To the Entered Numbes In Text Box          

                bool result = ***IsNumberInFile***(EnteredEmployeeId, "ManagerLoginId:", ManagersPath);
                if (result)
                {
                             //open new window
                }
                else
                {
                    MessageBox.Show("User Not Found");
                }
            }
        }
    }
}

This function will read through whole file and find if there is inserted code.这个 function 将读取整个文件并查找是否有插入的代码。 It will work with strings (as it is output of your text box) and will return only true or false (employee is or is not in file) not his name, surname etc.它将使用字符串(因为它是文本框的 output)并且只会返回 true 或 false(员工在或不在文件中)而不是他的名字、姓氏等。

static bool IsNumberInFile(string numberAsString, string LineName, string FileName)
    {
        var lines = File.ReadAllLines(FileName);

        foreach(var line in lines)
        {
            var trimmedLine = line.Replace(" ", ""); //To remove all spaces in file. Not expecting any spaces in the middle of number
            if (!string.IsNullOrEmpty(trimmedLine) && trimmedLine.Split(':')[0].Equals(LineName) && trimmedLine.Split(':')[1].Equals(numberAsString))
                return true;

        }
        return false;
    } 

//Example of use //使用示例

    String ManagersPath = @"C:\Users\Name\Visual Studios Project Custom Files\Employee Id's\Managers\Manager_Ids.txt"; //Path To Manager Logins

String EnteredEmployeeId;

private void textBox1_TextChanged(object sender, EventArgs e)
{

}

private void Employee_Id_TextBox_KeyPress(object sender, KeyPressEventArgs e)
{
    if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) &&           //Checks Characters entered are Numbers Only and allows them
        (e.KeyChar != '0'))
            {
                e.Handled = true;
            }
    else if (e.KeyChar == (char)13)                                         //Checks if The "Enter" Key is pressed
    {
        EnteredEmployeeId = Employee_Id_TextBox.Text;                         //Assigns EnteredEmployeeId To the Entered Numbes In Text Box           


        bool result = IsNumberInFile(EnteredEmployeeId, "ManagerLoginId" , ManagersPath) 
        if(result)
           //User is in file
        else
          //User is not in file
    }
}

} }

Short answer简答

Is your question about how to read your file?你的问题是关于如何阅读你的文件?

private bool ManagerExists(int managerId)
{
    return this.ReadManagers().Where(manager => manager.Id == managerId).Any();
}

private IEnumerable<Manager> ReadManagers()
{
    using (var reader = System.IO.File.OpenText(managersFileName))
    {
        while (!reader.EndOfStream)
        {
            string lineManagerName = reader.ReadLine();
            string lineMangerId = reader.ReadLine();

            string managerName = ExtractValue(lineManagerName);
            int managerId = Int32.Parse(ExtractValue(lineManagerId));

            yield return new Manager
            {
                Id = managerId,
                Name = managerName,
            }
    }
}

private string ExtractValue(string text)
{
    // the value of the read text starts after the space:
    const char separator = ' ';
    int indexSeparator = text.IndexOf(separator);
    return text.SubString(indexSeparator + 1);
}

Long Answer长答案

I see several problems in your design.我在你的设计中看到了几个问题。

The most important thing is that you intertwine your manager handling with your form.最重要的是你将你的经理处理与你的表格交织在一起。 You should separate your concerns .你应该分开你的关注点

Apparently you have the notion of a sequence of Managers, each Manager has a Name (first name, last name) and a ManagerId, and in future maybe other properties.显然,您有一系列管理器的概念,每个管理器都有一个名称(名字、姓氏)和一个 ManagerId,将来可能还有其他属性。

This sequence is persistable: it is saved somewhere, and if you load it again, you have the same sequence of Managers.这个序列是持久的:它被保存在某个地方,如果你再次加载它,你有相同的管理器序列。

In this version you want to be able to see if a Manager with a given ManagerId exists.在此版本中,您希望能够查看是否存在具有给定 ManagerId 的 Manager。 Maybe in future you might want more functionality, like fetching information of a Manager with a certain Id, or Fetch All managers, or let's go crazy: Add / Remove / Change managers!也许将来您可能需要更多功能,例如获取具有特定 Id 的经理的信息,或获取所有经理,或者让我们疯狂 go:添加/删除/更改经理!

You see in this description I didn't mention your Forms at all.你看在这个描述中我根本没有提到你的 Forms。 Because I separated it from your Forms, you can use it in other forms, or even in a class that has nothing to do with a Form, for instance you can use it in a unit test.因为我把它和你的Forms分开了,你可以在其他forms中使用,甚至可以在一个与Form无关的class中使用,比如你可以在单元测试中使用它。

I described what I needed in such a general from, that in future I might even change it.我以如此笼统的方式描述了我需要的东西,将来我什至可能会改变它。 Users of my persistable manager collection wouldn't even notice it: I can put it in a JSON file, or XML;我的持久管理器集合的用户甚至不会注意到它:我可以将它放在 JSON 文件或 XML 中; I can save the data in a Dictionary, a database, or maybe even fetch it from the inte.net.我可以将数据保存在字典、数据库中,甚至可以从互联网上获取。

All that users need to know, is that they have to create an instance of the class, using some parameters, and bingo, you can fetch Managers.用户需要知道的是,他们必须使用一些参数创建 class 的实例,然后宾果游戏,您可以获取经理。

You also give users the freedom to decide how the data is to be saved: if they want to save it in a JSON file, changes in your form class will be minimal.您还让用户可以自由决定如何保存数据:如果他们想将其保存在 JSON 文件中,则您的表单 class 中的更改将是最小的。

An object that stores sequences of objects is quite often called a Repository.存储对象序列的 object 通常称为存储库。

Let's create some classes:让我们创建一些类:

interface IManager
{
    public int Id {get;}
    public string Name {get; set;}
}

interface IManagerRepository
{
    bool ManagerExists(int managerId);

    // possible future extensions: Add / Retrieve / Update / Delete (CRUD)
    IManager Add(IManager manager);
    IManager Find(int managerId);
    void Update(IManager manager);
    void Delete(int ManagerId);
}

class Manager : IManager
{
    public Id {get; set;}
    public string Name {get; set;}
}

class ManagerFileRepository : IManagerRepository,
{
    public ManagerFileRepository(string fileName)
    {
         // TODO implement
    }

    // TODO: implement.
}

The ManagerFileRepository saves the managers in a file. ManagerFileRepository 将管理器保存在一个文件中。 It hides for the outside world how the file is internally structured.它向外界隐藏了文件的内部结构。 It could be your file format, it could be a CSV-file, or JSON / XML.它可以是您的文件格式,可以是 CSV 文件,或 JSON / XML。

I also separated an interface, so if you later decide to save the data somewhere else, for instance in a Dictionary (for unit tests), or in a database, users of your Repository class won't see the difference.我还分离了一个接口,所以如果您以后决定将数据保存在其他地方,例如在字典(用于单元测试)或数据库中,您的存储库 class 的用户将看不到差异。

Let's first see if you can use this class.先看看这个class能不能用。

class MyForm : Form
{
    const string managerFileName = ...
    private IManagerRepository ManagerRepository {get;}

    public MyForm()
    {
        InitializeComponent();

        this.ManagerRepository = new ManagerFileRepository(managerFileName);
    }

    public bool ManagerExists(int managerId)
    {
        return this.ManagerRepository.ManagerExists(managerId);
    }

Now let's handle your keyPress:现在让我们处理您的按键:

private void Employee_Id_TextBox_KeyPress(object sender, KeyPressEventArgs e)
{
    TextBox textBox = (TextBox)sender;
    ... // code about numbers and enter key

    int enteredManagerId = Int32.Parse(textBox.Text);
    bool managerExists = this.ManagerExists(enteredManagerId);

    if (managerExists) { ... }

} }

This code seems to do what you want in an easy way.这段代码似乎以一种简单的方式做你想做的事。 It looks transparent.它看起来是透明的。 The managerRepository is testable, reusable, simple to extend or change, because users won't notice this. managerRepository 是可测试的、可重用的、易于扩展或更改的,因为用户不会注意到这一点。 So the class looks good.所以 class 看起来不错。 Let's implement让我们实施

Implement ManagerFileRepository实施ManagerFileRepository

There are several ways to implement reading the file:有几种方法可以实现读取文件:

(1) Read everything at construction time (1) 在构建时阅读所有内容
and keep the read data in memory. If you add Managers they are not saved until you say so.并将读取的数据保留在 memory 中。如果您添加经理,则除非您这么说,否则它们不会被保存。 Advantages: after initial startup it is fast.优点:初始启动后速度很快。 You can make changes and later decide not to save them anyway, so it is just like editing any other file.您可以进行更改,然后决定不保存它们,所以这就像编辑任何其他文件一样。 Disadvantage: if your program crashes, you have lost your changes.缺点:如果您的程序崩溃,您将丢失更改。

(2) Read the file every time you need information (2) 每次需要信息时读取文件
Advantage: data is always up-to-date, even if others edited the file while your program runs.优点:数据始终是最新的,即使其他人在您的程序运行时编辑了文件。 If you change the manager collection it is immediately saved, so other can use it.如果您更改管理器集合,它会立即保存,以便其他人可以使用它。

Which solution you choose depends on the size of the file and the importance of never losing data.您选择哪种解决方案取决于文件的大小和永不丢失数据的重要性。 If you file contains millions of records, then maybe it wasn't very wise to save the data in a file.如果您的文件包含数百万条记录,那么将数据保存在文件中可能不是很明智。 Consider SQLite to save it in a small fairly fast database.考虑将 SQLite 保存在一个相当快的小型数据库中。

class ManagerFileRepository : IManagerRepository, IEnumerable<IManager>
{
    private readonly IDictionary<int, IManager> managers;

    public ManagerFileRepository(string FileName)
    {
        this.managers = ReadManagers(fileName);
    }

    public bool ManagerExists(int managerId)
    {
        return this.Managers.HasKey(managerId);
    }

    private static IEnumerable<IManager> ReadManagers(string fileName)
    {
         // See the short answer above
    }
}

Room for improvement改进空间

If you will be using your manager repository for more things, consider to let the repository implement ICollection<IManager> and IReadOnlyCollection<IManager> .如果您将使用管理器存储库做更多事情,请考虑让存储库实现ICollection<IManager>IReadOnlyCollection<IManager> This is quite simple:这很简单:

public IEnumerable<IManager> GetEnumerator()
{
    return this.managers.Values.GetEnumerator();
}
public void Add(IManager manager)
{
    this.managers.Add(manager.Id, manager);
}
// etc.

If you add functions to change the manager collection you'll also need a Save method:如果您添加函数来更改管理器集合,您还需要一个 Save 方法:

public void Save()
{
    using (var writer = File.CreateText(FullFileName))
    {
        const string namePrefix = "ManagerName: ";
        const string idPrefix = "ManagerLoginId: ";
        foreach (var manager in managers.Values)
        {
            string managerLine = namePrefix + manager.Name;
            writer.WriteLine(managerLine);

            string idLine = idPrefix + manager.Id.ToString();
            writer.WriteLine(idLine);
        }
    }
}

Another method of improvement: your file structure .另一个改进方法:你的文件结构 Consider using a more standard file structure: CSV, JSON, XML. There are numerous NUGET packages (CSVHelper, NewtonSoft.Json) that makes reading and writing Managers much more robust.考虑使用更标准的文件结构:CSV、JSON、XML。有许多 NUGET 包(CSVHelper、NewtonSoft.Json)使读写管理器更加健壮。

Summary概括

Because you separated the concerns of persisting your managers from your form, you can reuse the manager repository, especially if you need functionality to Add / Retrieve / Update / Delete managers.因为您将保留管理器的关注点与表单分开了,所以您可以重用管理器存储库,尤其是当您需要添加/检索/更新/删除管理器的功能时。

Because of the separation it is much easier to unit test your functions.由于分离,对您的功能进行单元测试要容易得多。 And future changes won't hinder users of the repository, because they won't notice that the data has changed.未来的更改不会妨碍存储库的用户,因为他们不会注意到数据已更改。

If your Manager_Ids.txt is in the following format, you can use File.ReadLine() method to traverse the text and query it.如果你的Manager_Ids.txt是如下格式,你可以使用File.ReadLine()方法遍历文本进行查询。

ManagerName: FirstName_LastName1
ManagerLoginId: 12345
ManagerName: FirstName_LastName2
ManagerLoginId: 23456
...

Here is the demo that traverse the.txt.下面是遍历.txt的demo。

string ManagersPath = @"D:\Manager_Ids.txt";
string EnteredEmployeeId;

private void textBox_id_KeyDown(object sender, KeyEventArgs e)
{
    int counter = 0;
    bool exist = false;
    string line;
    string str = "";

    if (e.KeyCode == Keys.Enter)
    {
        EnteredEmployeeId = textBox_id.Text;

        System.IO.StreamReader file =
            new System.IO.StreamReader(ManagersPath);
        while ((line = file.ReadLine()) != null)
        {
            str += line + "|";

            if (counter % 2 != 0)
            {
                if (str.Split('|')[1].Split(':')[1].Trim() == EnteredEmployeeId)
                {
                    str = str.Replace("|", "\n");
                    MessageBox.Show(str);
                    exist = true;
                    break;
                }
                str = "";
            }
            counter++;
        }

        if (!exist)
        {
            MessageBox.Show("No such id");
        }

        file.Close();
    }
}

Besides, I recommend to use "xml", "json" or other formats to serialize the data.此外,我建议使用“xml”、“json”或其他格式来序列化数据。 About storing the data in "xml", you can refer to the following simple demo.关于将数据存储在“xml”中,可以参考下面的简单demo。

<?xml version="1.0"?>  
<Managers>  
  <Manager>  
    <ManagerName>FirstName_LastName1</ManagerName>  
    <ManagerLoginId>12345</ManagerLoginId>  
  </Manager>  
  <Manager>  
    <ManagerName>FirstName_LastName2</ManagerName>  
    <ManagerLoginId>23456</ManagerLoginId>  
  </Manager>  
</Managers> 

And then use LINQ to XML to query the id.然后用LINQ到XML查询id。

string ManagersPath = @"D:\Manager_Ids.xml";
string EnteredEmployeeId;

private void textBox_id_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Enter)
    {
        EnteredEmployeeId = textBox_id.Text;

        XElement root = XElement.Load(ManagersPath);
        IEnumerable<XElement> manager =
            from el in root.Elements("Manager")
            where (string)el.Element("ManagerLoginId") == EnteredEmployeeId
            select el;
        if(manager.Count() == 0)
        {
            MessageBox.Show("No such id");
        }
        foreach (XElement el in manager)
            MessageBox.Show("ManagerName: " + (string)el.Element("ManagerName") + "\n"
                + "ManagerLoginId: " + (string)el.Element("ManagerLoginId"));
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM