whatthefish
whatthefish

Reputation: 357

Prinitng matching information from 2 files in Java

I am trying to write a program that checks two files and prints the common contents from both the files.

Example of the file 1 content would be:

James 1
Cody 2
John 3

Example of the file 2 content would be:

1 Computer Science
2 Chemistry
3 Physics

So the final output printed on the console would be:

James Computer Science
Cody Chemistry
John Physics

Here is what I have so far in my code:

public class Filereader {
    public static void main(String[] args) throws Exception {

    File file = new File("file.txt");
    File file2 = new File("file2.txt");
    BufferedReader reader = new BufferedReader(new FileReader(file));
    BufferedReader reader2 = new BufferedReader(new FileReader(file2));
    String st, st2;
    while ((st = reader.readLine()) != null) { 
         System.out.println(st);
    }
    while ((st2 = reader2.readLine()) != null) {
            System.out.println(st2);
    }
    reader.close();

    reader2.close();

    }
}

I am having trouble in figuring out how to match the file contents, and print only the student name and their major by matching the student id in each of the file. Thanks for all the help.

Upvotes: 0

Views: 366

Answers (5)

Mạnh Quyết Nguyễn
Mạnh Quyết Nguyễn

Reputation: 18235

What you want is to organize your text file data into map, then merge their data. This will work even if your data are mixed, not in order.

public class Filereader {
    public static void main(String[] args) throws Exception {

    File file = new File("file.txt");
    File file2 = new File("file2.txt");
    BufferedReader reader = new BufferedReader(new FileReader(file));
    BufferedReader reader2 = new BufferedReader(new FileReader(file2));
    String st, st2;

    Map<Integer, String> nameMap = new LinkedHashMap<>();
    Map<Integer, String> majorMap = new LinkedHashMap<>();

    while ((st = reader.readLine()) != null) { 
         System.out.println(st);
         String[] parts = st.split(" "); // Here you got ["James", "1"]
         String name = parts[0];
         Integer id = Integer.parseInt(parts[1]);
         nameMap.put(id, name);
    }
    while ((st2 = reader2.readLine()) != null) {
         System.out.println(st2);
         String[] parts = st2.split(" ");
         String name = parts[1];
         Integer id = Integer.parseInt(parts[0]);
         majorMap.put(id, name);
    }
    reader.close();
    reader2.close();

    // Combine and print
    nameMap.keySet().stream().forEach(id -> {
      System.out.println(nameMap.get(id) + " " + majorMap.get(id));
    })

    }
}

Upvotes: 0

user5063151
user5063151

Reputation:

If you change the order such that the number comes first in both files, you can read both files into a HashMap then create a Set of common keys. Then loop through the set of common keys and grab the associated value from each Hashmap to print:

My solution is verbose but I wrote it that way so that you can see exactly what's happening.

import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import java.io.File;
import java.util.Scanner;

class J {

  public static Map<String, String> fileToMap(File file) throws Exception {

    // TODO - Make sure the file exists before opening it

    // Scans the input file
    Scanner scanner          = new Scanner(file);

    // Create the map
    Map<String, String> map = new HashMap<>();

    String   line;
    String   name;
    String  code;
    String[] parts = new String[2];

    // Scan line by line
    while (scanner.hasNextLine()) {

      // Get next line
      line = scanner.nextLine();

      // TODO - Make sure the string has at least 1 space

      // Split line by index of first space found
      parts = line.split(" ", line.indexOf(' ') - 1);

      // Get the class code and string val
      code = parts[0];
      name = parts[1];

      // Insert into map
      map.put(code, name);
    }

    // Close input stream
    scanner.close();

    // Give the map back
    return map;
  }

  public static Set<String> commonKeys(Map<String, String> nameMap,
                                       Map<String, String> classMap) {

    Set<String> commonSet = new HashSet<>();

    // Get a set of keys for both maps
    Set<String> nameSet  = nameMap.keySet();
    Set<String> classSet = classMap.keySet();

    // Loop through one set
    for (String key : nameSet) {

      // Make sure the other set has it
      if (classSet.contains(key)) {

        commonSet.add(key);
      }
    }

    return commonSet;
  }

  public static Map<String, String> joinByKey(Map<String, String> namesMap,
                                              Map<String, String> classMap,
                                              Set<String> commonKeys) {

    Map<String, String> map = new HashMap<String, String>();

    // Loop through common keys
    for (String key : commonKeys) {

      // TODO - check for nulls if get() returns nothing

      // Fetch the associated value from each map
      map.put(namesMap.get(key), classMap.get(key));
    }

    return map;
  }

  public static void main(String[] args) throws Exception {

    // Surround in try catch
    File names   = new File("names.txt");
    File classes = new File("classes.txt");

    Map<String, String> nameMap  = fileToMap(names);
    Map<String, String> classMap = fileToMap(classes);
    Set<String> commonKeys       = commonKeys(nameMap, classMap);

    Map<String, String> nameToClass = joinByKey(nameMap, classMap, commonKeys);

    System.out.println(nameToClass);

  }
}

names.txt

1 James
2 Cody
3 John
5 Max

classes.txt

1 Computer Science
2 Chemistry
3 Physics
4 Biology

Output:

{Cody=Chemistry, James=Computer, John=Physics}

Notes:

  1. I added keys in classes.txt and names.txt that purposely did not match so you see that it does not come up in the output. That is because the key never makes it into the commonKeys set. So, they never get inserted into the joined map.

  2. You can loop through the HashMap if you want my calling map.entrySet()

Upvotes: 0

Gaspar
Gaspar

Reputation: 1601

You can use the other answers and make an object to every file, like tables in databases.

public class Person{
   Long id;
   String name;
   //getters and setters
}
public class Course{
   Long id;
   String name;
   //getters and setters
}

Them you have more control with your columns and it is simple to use. Further you will use an ArrayList<Person> and an ArrayList<Course> and your relation can be a variable inside your objects like courseId in Person class or something else.

if(person.getcourseId() == course.getId()){
   ...
}

Them if the match is the first number of the files use person.getId() == course.getId(). Ps: Do not use split(" ") in your case, because you can have other objects with two values i.e 1 Computer Science.

Upvotes: 1

ernest_k
ernest_k

Reputation: 45329

Combining the NIO Files and Stream API, it's a little simpler:

public static void main(String[] args) throws Exception {
    Map<String, List<String[]>> f1 = Files
            .lines(Paths.get("file1"))
            .map(line -> line.split(" "))
            .collect(Collectors.groupingBy(arr -> arr[1]));
    Map<String, List<String[]>> f2 = Files
            .lines(Paths.get("file2"))
            .map(line -> line.split(" "))
            .collect(Collectors.groupingBy(arr -> arr[0]));

    Stream.concat(f1.keySet().stream(), f2.keySet().stream())
        .distinct()
        .map(key -> f1.get(key).get(0)[0] + " " + f2.get(key).get(0)[1])
        .forEach(System.out::println);
}

As can easily be noticed in the code, there are assumptions of valid data an of consistency between the two files. If this doesn't hold, you may need to first run a filter to exclude entries missing in either file:

Stream.concat(f1.keySet().stream(), f2.keySet().stream())
    .filter(key -> f1.containsKey(key) && f2.containsKey(key))
    .distinct()
    ...

Upvotes: 0

Makoto
Makoto

Reputation: 106470

You should read these files at the same time in sequence. This is easy to accomplish with a single while statement.

while ((st = reader.readLine()) != null && (st2 = reader2.readLine()) != null) {
    // print both st and st2
}

The way your code is written now, it reads one file at a time, printing data to the console from each individual file. If you want to meld the results together, you have to combine the output of the files in a single loop.


Given that the intention may also be that you have an odd-sized file in one batch but you do have numbers to correlate across, or the numbers may come in a nonsequential order, you may want to store these results into a data structure instead, like a List, since you know the specific index of each of these values and know where they should fit in.

Upvotes: 0

Related Questions