How can I update specific parts of a text file in java?



This program is supposed to take in user input about a players name, assists, games played, scores, etc and print it in a .txt file. When the updateData(); method is called I want to be able to ask the user for the players name and what data they want to update, then i should be able to edit that specific part of the text. how could i go about doing this?

Main Class

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Scanner;

public class TextReader {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("/Users/Coding/Desktop/myFile.txt").toAbsolutePath();
        try (Scanner scan = new Scanner(System.in);
             BufferedReader fileReader = new BufferedReader(new FileReader(String.valueOf(path)));
             BufferedWriter fileWriter = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {

            Reader reader = new Reader(scan, path, fileWriter, fileReader);
            reader.menu();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}



Reader Class

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Scanner;

public class Reader {

    Path path;
    Scanner scan;
    BufferedWriter fileWriter;
    BufferedReader fileReader;


    Reader(Scanner scan, Path path, BufferedWriter fileWriter, BufferedReader fileReader) {

        this.scan = scan;
        this.path = path;
        this.fileWriter = fileWriter;
        this.fileReader = fileReader;
    }

    public void menu() throws IOException {
        String task;

        do{
            System.out.print("What would you like to do today?: ");
            task = scan.nextLine();
            switch (task) {
                case "1":
                    addData();
                    break;
                case "2":
                    updateData();
                    break;
                case "6":
                    System.out.println("Goodbye!");
                    System.exit(0);
            }
        }while(!task.equals("6"));


    }

    void addData() throws IOException {
        boolean cont;

        DateTimeFormatter log = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
        LocalDateTime time = LocalDateTime.now();
        String logTime = log.format(time);

        do try {
            System.out.print("Enter Name of Player: ");
            String playerName = scan.nextLine();

            System.out.print("Enter Number of Games Played: ");
            int gamesPlayed = Integer.parseInt(scan.nextLine());

            System.out.print("Enter Number of Goals Made: ");
            int goals = Integer.parseInt(scan.nextLine());

            System.out.print("Enter Number of Assists Made: ");
            int assists = Integer.parseInt(scan.nextLine());

            System.out.print("Enter Number of Points Scored: ");
            int points = Integer.parseInt(scan.nextLine());

            System.out.print("Enter Number of Saves Made: ");
            int saves = Integer.parseInt(scan.nextLine());

            System.out.print("Enter Number of Shots Made: ");
            int shotsOnGoal = Integer.parseInt(scan.nextLine());

            fileWriter.write(
                    playerName + " " + gamesPlayed + " " + goals + " " +
                            assists + " " + points + " " + saves + " " + shotsOnGoal + " (" + logTime + ") n");
            cont = false;
        } catch(NumberFormatException e){
            System.out.println("Enter Valid Input");
            cont = true;
        }while(cont);


    }

    void updateData() throws IOException {
        System.out.print("Enter Player Name To Edit Data: ");
        String playerName = scan.nextLine();

        System.out.print("Enter Stat You Want To Change: ");
        String stat = scan.nextLine().toLowerCase().trim();

        if(fileReader.readLine().contains(playerName)){
            String statSearch = fileReader.readLine();
            

            }
        }

    }


}


Text File Format:

Name GP G A P S S%

Bobby 2 3 6 14 7 50

George 1 3 14 2 9 23

So if the user wanted to edit Name: George, type: Assists, the value 14 beside Georges name only would be edited

I have tried using an if statement to locate the string in the text and append it but I could not figure out how to only change the specified number without changing all the numbers found. Ex: if in the example above 14 is appended both would be changed instead of the one

Answer

If you are allowed for this project (i.e., not a school assignment), I recommend using JSON, YAML, or XML. There are too many Java libraries to recommend for using these types of files, but you can search “Java JSON library” for example.

First, need to address some issues…

It’s not good practice to put Scanner scan = new Scanner(System.in); in a try-with-resource. It will auto-close System.in and won’t be useable after being used in your Reader class. Instead, just do this:

Scanner scan = new Scanner(System.in);
Reader reader = new Reader(scan, path, fileWriter, fileReader);

Or, even better, don’t pass it to the constructor, but just set scan to it in the constructor as this.scan = new Scanner(System.in);

Next, for fileReader, you can just initialize it similarly as you did for fileWriter:

BufferedReader fileReader = Files.newBufferedReader(path, StandardCharsets.UTF_8);

Next, this line:

BufferedWriter fileWriter = Files.newBufferedWriter(path, StandardCharsets.UTF_8)

Every time this program is run, this line will overwrite the file to empty, which is probably not what you want. You could add StandardOpenOption.APPEND, but then this means you’ll only write to the end of the file.

When you update data, you also have the issue that you’ll need to “push” down all of the data that comes after it. For example:

Bobby 1 2 3 4 5
Fred 1 2 3 4 5

If you change the name Bobby to something longer like Mr. President, then it will overwrite the data after it.

While there are different options, the best and simplest is to just read the entire file and store each bit of data in a class (name, scores, etc.) and then close the fileReader.

Then when a user updates some data, change that data (instance variables) in the class and then write all of that data to the file.

Here’s some pseudo-code:

class MyProg {
  // This could be a Map/HashMap instead.
  // See updateData().
  public List<Player> players = new ArrayList<>();

  public void readData(String filename) throws IOException {
    Path path = Paths.get(filename);
    
    try(BufferedReader fileReader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
      // Read each Player (using specific format)
      // and store in this.players
    }
  }

  public void writeData(String filename) throws IOException {
    Path path = Paths.get(filename);

    try(BufferedWriter fileWriter = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
      // Write each Player from this.players in specific format
    }
  }

  public void updateData() {
    // 1. Find user-requested Player from this.players
    // 2. Update that specific Player class
    // 3. Call writeData()
    
    // If you are familiar with Maps, then it would be faster
    // to use a Map/HashMap with the key being the player's name.
  }
}

class Player {
  public String name;
  public int games;
  public int goals;
  //...
}


Source: stackoverflow