Victor Alam
Victor Alam

Reputation: 1

Reading in from a file into an arraylist then dividing array list into multiple strings so I can parse them individually

So for part of my assignment I have to pull information from a file outside of Java. I have already gotten that part done. The problem is that I am not sure how to actually put the strings from the files into a variable or loop that will work for the next part. In the following code I need to replace the part of the code that have that says Item = Tomato... with singular lines from the outfile. I am not sure how to do this. My major concern would be making sure it was not hardcoded for each line, I am guessing it will involve looping through each line in some way or form. Any help would be great.

How I originally added the items in hardcoded, as opposed to what I want to do which is input them from an outfile:

    list.add(new Item("Ketchup", 1.00, 10, 2.00, itemType.FOOD));
    list.add(new Item("Mayo", 2.00, 20, 3.0, itemType.FOOD));
    list.add(new Item("Bleach", 3.00, 30, 4.00, itemType.CLEANING));
    list.add(new Item("Lysol", 4.00, 40, 5.00, itemType.CLEANING));

Code

    Scanner s = new Scanner(new File("inventory.out"));

    ArrayList<String> inventoryList = new ArrayList<String>();

    while (s.hasNext()){
        inventoryList.add(s.next());
    }
    s.close();

    System.out.println(inventoryList);

    String item = "Tomato,30,1.25,6.50";// input String like the one you would read from a file

    String delims = "[,]"; //delimiter - a comma is used to separate your tokens (name, qty,cost, price)

    String[] tokens = item.split(delims); // split it into tokens and place in a 2D array.

    for (int i=0; i < 4; i++) {
        System.out.println(tokens[i]); // print the tokens.
    }

    String name = tokens[0]; System.out.println(name);

    int qty = Integer.parseInt(tokens[1]);System.out.println(qty);

    double cost = Double.parseDouble(tokens[2]);System.out.println(cost); 

Console output right now:

[Ketchup,1.00,10,2.00,itemType.FOOD, Mayo,2.00,20,3.00,itemType.FOOD, Bleach,3.00,30,4.00,itemType.CLEANING, Lysol,4.00,40,5.00,itemType.CLEANING]

Contents of the outfile:

Ketchup,1.00,10,2.00,itemType.FOOD
Mayo,2.00,20,3.00,itemType.FOOD
Bleach,3.00,30,4.00,itemType.CLEANING
Lysol,4.00,40,5.00,itemType.CLEANING

Upvotes: 0

Views: 656

Answers (3)

sn42
sn42

Reputation: 2444

Implementation

public static void main(String[] args) throws IOException {
    Path path = Paths.getPath("inventory.out");
    List<Item> items = readItems(path);
    for (Item item : items) {
        System.out.printf("Item (name='%s', capacity=%d,  cost=%f, price=%f)\n",
            item.getName(), item.getCapacity(), item.getCost(), item.getPrice());
    }
}
public class Item {
    private final String name;
    private final int quantity;
    private final double cost;
    private final double price;

    public Item (String name, int capacity, double cost, double price) {
        this.name = name;
        this.capacity = capacity;
        this.cost = cost;
        this.price = price;
    }

    // Getters omitted.
}

public class ItemUtils {
    /**
     * Read all lines from a file and maps them to items.
     *
     * @param path the path to the file, non-null
     * @return the list of items read from the file
     * @throws IOException if an I/O error occurs reading from the file or a malformed or unmappable byte sequence is read
     * @throws CustomRuntimeException if a line can't be mapped to an item
     */
    public static List<Item> readItems(Path path) throws IOException {
        Objects.requireNonNull(path);

        return Files.readAllLines(path, StandardCharsets.UTF_8)
            .stream()
            .map(ItemUtils::mapStringToItem)
            .collect(Collectors.toList());

    }

    /**
     * Maps a string to an item.
     *
     * @param str the string to map, non-null
     * @return the mapped item
     * @throws CustomRuntimeException if the string can't be mapped to an item
     */
    private static Item mapStringToItem(String str) {
        String[] tokens = str.split(",");

        if (tokens.length != 4) {
            String msg = String.format("Invalid item: 4 tokens expected, %d tokens found", tokens.length);
            throw new CustomRuntimeException(msg);
        }

        try {
            String name = tokens[0];
            int quantity = Integer.parseInt(tokens[1]);
            double cost = Double.parseDouble(tokens[2]);
            double price = Double.parseDouble(tokens[3]);
            return new Item(name, quantity, cost, price);
        } catch (NumberFormatException e) {
            throw new CustomRuntimeException("Invalid item: Type conversion failed", e);
        }
    }

    private ItemUtils() {
        // Utility class, prevent instantiation.
    }
}

/**
 * A custom runtime exception. Should be renamed to a more specific name.
 */
public class CustomRuntimeException extends RuntimeException {
    public CustomRuntimeException(String msg) {
       super(msg);
    }

    public CustomRuntimeException(String msg, Throwable e) {
        super(msg, e);
    }

Explanation

The readLines method uses Files.readAllLines(...) to read all lines into a list of strings, where each string corresponds to a single line. Then I process the strings of this list with the Java 8 Stream API, the list class provides a stream() method returning a Stream<String>:

If you want to perform some actions on a collections object for example: filtering, sorting or manipulating each element on that object based on some condition, you can use the java 8 stream feature to complete your requirement easily with less code.

Here you can find a more practical explanation of streams.

The stream's map(...) method takes the method reference of mapStringToItem as its argument. When you look at mapStringToItem's signature you see it takes a single string as an argument and returns an Item object. So you can read the .map(...) invocation as "map each line from the file to an item using the method mapStringToItem". Then I collect all items from the stream and put them into a new list with the collect(...) method.

Lets have a look at the mapStringToItem method, which is using your approach of splitting the items values: First we split the line at each , returning an array of strings. Currently the item class consists of 4 attributes which should be read: The name, the capacity, the cost and the price. So we check if the string array has the appropriate length, if not this implementation throws an exception. If we have 4 strings (splittet by ,) we can start parsing the string values to the appriate data types, throwing an exception if the type conversion fails. Last but not least we return an item with the parsed values.

Comments

Note that I would advice against using floating point variables to store money values. Have a look at Joda-Money.

It might be worth to search for libraries which handle the serialization of your data classes for you. If you don't mind changing your format to JSON jackson-databind or similar libraries can be a solution.

Upvotes: 0

Stephen C
Stephen C

Reputation: 719576

You need to have a clear strategy.

Your input consists of lines, which in turn consist of fields. You are (presumably) aiming to process the data as "records". You can do it a couple of ways:

  1. Use the scanner to read lines, and split / scan / tokenize each line into record fields.
  2. Scan the entire input stream as a sequence of tokens or values, and reassemble the records on the fly.

Either approach will work. But you need to decide which approach you are going to take ... and stick to that approach.

(If you just start writing or copying code without a clear strategy, you are liable to end up with a mess, or code that you don't understand, or both.)

Upvotes: 1

Raymo111
Raymo111

Reputation: 584

Try removing String item = "Tomato,30,1.25,6.50"; and replacing all the 'item's after that with inventoryList.get(thepositionatwhichyouwanttogetanitemfrom);

Upvotes: 0

Related Questions