如何更新文本文件中的特定数字?

How do I update a specific number in a text file?

提问人:Alem Beskovic 提问时间:6/9/2023 更新时间:6/10/2023 访问量:40

问:

我正在编写一个房地产 gui 程序,该程序从租户那里获取信息,例如名字、姓氏、公寓等,并将这些值存储在文本文件中。我为不同的选项提供了不同的类,例如删除租户、更新租户、添加租户和查看所有租户。我有用于添加租户、删除租户和查看租户的类,但在更新类时遇到问题。添加这些租户的方式是通过数组列表,该数组列表采用这些不同的变量,并使用 toString 在文本文件中逐行上传它们。每个租户信息位于不同的行上。我在更新类中尝试做的是,当存入资金时,该特定租户的余额设置为 MonthlyRent - deposit = balance。我遇到的问题是试图弄清楚如何读取 monthlyRent 和余额,将它们设置为 2 变量,然后进行计算并更新该特定部分。我知道我必须删除文件并重新更新文件,我只是不知道如何读取该特定部分并将它们设置为变量。

//////////////////////////////////////////////////////////////////////////////////////////


public class Person extends JFrame{
    File log = new File("tenantData.txt");
    
    
    static ArrayList<Person> tenantData = new ArrayList<Person>();
    public String firstName;
    public String lastName;
    public int apartmentNumber;
    public String streetAddress;
    public int streetNumber;
    public String town;
    public String state;
    public float balance;
    public float deposit;
    public float monthRent;


Person( String firstName, String LastName, int apartmentNumber, String streetAddress, int streetNumber, String town, String state, float monthRent, float balance)
     {
        
         this.firstName = firstName;
         this.lastName = LastName;
         this.apartmentNumber = apartmentNumber;
         this.streetAddress = streetAddress;
         this.streetNumber = streetNumber;
         this.town = town;
         this.state = state;
         this.monthRent = monthRent;
         this.balance = balance;
         
     }
     @Override
     public String toString() {
            String str = firstName + " " + lastName + ", apartment" + apartmentNumber + ", " +streetNumber+ " " +streetAddress+ ", " +town+ ", " +state+
     ". Monthly Rent: "+ monthRent+  " Balance: " + balance + "\n";
            return str;
    }


这是主类的变量和构造函数

public void buildPanel3() {
            JPanel panel = new JPanel();
            ButtonHandler4 b = new ButtonHandler4();
            label = new JLabel("Enter first and last name of tenant you would like to update");
            field = new JTextField(25);
            label2 = new JLabel("Enter the amount that the tenant has payed");
            paymentAmount = new JTextField(4);
            update = new JButton(updateTenant);
            update.addActionListener(b);
            panel.add(label);
            panel.add(field);
            panel.add(label2);
            panel.add(paymentAmount);
            panel.add(update);
            add(panel);
        }
        public class ButtonHandler4 implements ActionListener{
            public void actionPerformed(ActionEvent e) {
                String str = e.getActionCommand();
                if(str.equals(updateTenant)) {
                    String line = field.getText();
                    String payment = paymentAmount.getText();
                    String[] parts = line.split(" ");
                    String firstName = parts[0];
                    String lastName = parts[1];

这是 Update 类的生成面板和按钮处理程序。我需要弄清楚如何首先在文件中搜索名字和姓氏,然后使用付款变量进行更新。

Java 数组 文件 arraylist file-io

评论

0赞 varocarbas 6/9/2023
对于数据库来说,这似乎是一个完美的案例。文件的速度要慢得多,并且存在您精确描述的那种问题:它们不太擅长处理特定值。对于更新行中的值,过程应类似于删除过程:将所有内容读入内存(例如,ArrayList),删除/修改给定元素,并将所有行写回文件。如果您更喜欢坚持使用这种格式,则最好通过尽可能长时间地将所有内容保留在内存中来最大程度地减少文件的读/写。
0赞 g00se 6/9/2023
@varocarbas不重新发明数据库是正确的。最好使用一种技能来获得技能,而不是重新发明一种技能。如果你决心避免这种情况,那么你的朋友就是new ArrayList<>(Files.readAllLines...

答:

-1赞 Reilas 6/10/2023 #1

为了调整解决方案,我不得不对一些变量进行重新排序。

我将 logtenantData 移动到另一个类。

我的第一个建议是使用不同的分隔符来分隔租户数据 .txt 文件中的值。
这将使与文件之间的解析变得更加容易,因为数据包含逗号。

您可以改用制表符
这是一个参考,维基百科 - 制表符分隔值

由于您正在使用 toString 来生成输出,因此可以将其替换为以下内容。

@Override
public String toString() {
    return firstName + ' ' + lastName + '\t'
        + apartmentNumber + '\t'
        + streetNumber + ' ' + streetAddress + '\t'
        + town + ", " + state + '\t'
        + monthRent + '\t'
        + balance;
}

此外,还需要一种方法将行转换为 Person 对象。
可以向 Person 类添加静态分析方法。

static Person parse(String string) {
    Person person = new Person();
    String[] strings = string.split("\t", -1);
    int indexOf = strings[0].indexOf(' ');
    person.firstName = strings[0].substring(0, indexOf);
    person.lastName = strings[0].substring(indexOf + 1);
    person.apartmentNumber = Integer.parseInt(strings[1]);
    indexOf = strings[2].indexOf(' ');
    person.streetNumber = Integer.parseInt(strings[2].substring(0, indexOf));
    person.streetAddress = strings[2].substring(indexOf + 1);
    indexOf = strings[3].indexOf(", ");
    person.town = strings[3].substring(0, indexOf);
    person.state = strings[3].substring(indexOf + 2);
    person.monthRent = Float.parseFloat(strings[4]);
    person.balance = Float.parseFloat(strings[5]);
    return person;
}

而且,我们需要一种方法来比较 Person 对象。
这也是在 Person 类中。

boolean equals(Person person) {
    return firstName.equals(person.firstName)
        && lastName.equals(person.lastName)
        && apartmentNumber == person.apartmentNumber
        && streetNumber == person.streetNumber
        && streetAddress.equals(person.streetAddress)
        && town.equals(person.town)
        && state.equals(person.state);
}

从这里,您需要创建功能来读取文件,并找到正确的行。
有几种方法可以实现这一点。我通常使用 BufferedReader 类,因为它提供了 readLine 方法。
您还可以使用 Scanner 类,NIO 框架也提供了 PathsFiles 类——实际上,我在下面使用它们来编写文件。

我将此方法与 logtenantData 变量一起放在另一个类中。

请注意,由于 Person 对象的唯一性依赖于许多变量,因此我使用 Person 对象作为要匹配的参数。
而不是为每个单独的值设置一个参数。

/** @return null if not found */
Person find(Person person) throws IOException {
    try (BufferedReader reader = new BufferedReader(new FileReader(log))) {
        String line;
        Person value;
        while ((line = reader.readLine()) != null) {
            if (person.equals(value = Person.parse(line)))
                return value;
        }
        return null;
    }
}

因此,您可以根据需要从此处更新 Person 对象的值。

其中,您需要一种方法将数据写回文件中。

我发现 Google AI 有一个有趣的方法,它建议我使用 String#replace 来覆盖原始值。

void save(Person original, Person updated) throws IOException {
    String string = new String(Files.readAllBytes(log.toPath()));
    string = string.replace(original.toString(), updated.toString());
    try (FileWriter writer = new FileWriter(log)) {
        writer.write(string);
        writer.flush();
    }
}

下面是一个基本示例,我在其中创建了包含 3 个 Person 对象的文件,然后更新了一个对象并保存它。

tenantData = new ArrayList<>();
tenantData.add(new Person("firstNameA", "lastNameA", 1, "addressA", 1, "townA", "stateA", 1, 1));
tenantData.add(new Person("firstNameB", "lastNameB", 1, "addressB", 1, "townB", "stateB", 2, 2));
tenantData.add(new Person("firstNameC", "lastNameC", 1, "addressC", 1, "townC", "stateC", 3, 3));

try (FileWriter writer = new FileWriter("files/tenantData.txt")) {
    for (Person person : tenantData)
        writer.write(person.toString() + System.lineSeparator());
    writer.flush();
}

Example example = new Example();

System.out.println("original");
for (String line : Files.readAllLines(example.log.toPath()))
    System.out.println(line);

Person person = example.find(new Person("firstNameB", "lastNameB", 1, "addressB", 1, "townB", "stateB", 0, 0));
Person copy = Person.parse(person.toString());
person.balance += 10;

example.save(copy, person);

System.out.println();
System.out.println("updated");
for (String line : Files.readAllLines(example.log.toPath()))
    System.out.println(line);

输出

original
firstNameA lastNameA    1   1 addressA  townA, stateA   1.0 1.0
firstNameB lastNameB    1   1 addressB  townB, stateB   2.0 2.0
firstNameC lastNameC    1   1 addressC  townC, stateC   3.0 3.0

updated
firstNameA lastNameA    1   1 addressA  townA, stateA   1.0 1.0
firstNameB lastNameB    1   1 addressB  townB, stateB   2.0 12.0
firstNameC lastNameC    1   1 addressC  townC, stateC   3.0 3.0

而且,这是完整的代码。

public class Example {
    File log = new File("tenantData.txt");
    static ArrayList<Person> tenantData = new ArrayList<Person>();

    /** @return null if not found */
    Person find(Person person) throws IOException {
        try (BufferedReader reader = new BufferedReader(new FileReader(log))) {
            String line;
            Person value;
            while ((line = reader.readLine()) != null) {
                if (person.equals(value = Person.parse(line)))
                    return value;
            }
            return null;
        }
    }

    void save(Person original, Person updated) throws IOException {
        String string = new String(Files.readAllBytes(log.toPath()));
        string = string.replace(original.toString(), updated.toString());
        try (FileWriter writer = new FileWriter(log)) {
            writer.write(string);
            writer.flush();
        }
    }

    static class Person {
        public String firstName;
        public String lastName;
        public int apartmentNumber;
        public String streetAddress;
        public int streetNumber;
        public String town;
        public String state;
        public float balance;
        public float deposit;
        public float monthRent;

        Person() {
        }

        Person(String firstName, String LastName, int apartmentNumber, String streetAddress, int streetNumber, String town, String state, float monthRent, float balance) {

            this.firstName = firstName;
            this.lastName = LastName;
            this.apartmentNumber = apartmentNumber;
            this.streetAddress = streetAddress;
            this.streetNumber = streetNumber;
            this.town = town;
            this.state = state;
            this.monthRent = monthRent;
            this.balance = balance;

        }

        static Person parse(String string) {
            Person person = new Person();
            String[] strings = string.split("\t", -1);
            int indexOf = strings[0].indexOf(' ');
            person.firstName = strings[0].substring(0, indexOf);
            person.lastName = strings[0].substring(indexOf + 1);
            person.apartmentNumber = Integer.parseInt(strings[1]);
            indexOf = strings[2].indexOf(' ');
            person.streetNumber = Integer.parseInt(strings[2].substring(0, indexOf));
            person.streetAddress = strings[2].substring(indexOf + 1);
            indexOf = strings[3].indexOf(", ");
            person.town = strings[3].substring(0, indexOf);
            person.state = strings[3].substring(indexOf + 2);
            person.monthRent = Float.parseFloat(strings[4]);
            person.balance = Float.parseFloat(strings[5]);
            return person;
        }

        boolean equals(Person person) {
            return firstName.equals(person.firstName)
                && lastName.equals(person.lastName)
                && apartmentNumber == person.apartmentNumber
                && streetNumber == person.streetNumber
                && streetAddress.equals(person.streetAddress)
                && town.equals(person.town)
                && state.equals(person.state);
        }

        @Override
        public String toString() {
            return firstName + ' ' + lastName + '\t'
                + apartmentNumber + '\t'
                + streetNumber + ' ' + streetAddress + '\t'
                + town + ", " + state + '\t'
                + monthRent + '\t'
                + balance;
        }
    }
}