0

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

3 Answers3

1

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.)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Thanks for the reply. And I believe the first approach is what I am trying to do. I edited my original post to show how I was originally hard coding items in in the first place to better show what I mean. – Victor Alam Apr 08 '18 at 01:53
  • If you trying to do it the first way, then you have coded it incorrectly. Your first loop needs to read lines not tokens, and the list needs to hold lines not individual tokens. Look at the javadoc for Scanner.readLine – Stephen C Apr 08 '18 at 02:16
0

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

Raymo111
  • 514
  • 8
  • 24
  • What would be my options in regard to what the position in which i want to get the item from – Victor Alam Apr 08 '18 at 01:38
  • That would be which line in the file the item came from. And do upvote and accept my answer if you think this helps. – Raymo111 Apr 08 '18 at 01:41
  • I still do not get how this really works. Thank for your help by the way – Victor Alam Apr 08 '18 at 01:50
  • .get is an ArrayList function. I suggest learning what ArrayLists are before continuing. And do upvote and accept my answer if you think this helps. – Raymo111 Apr 08 '18 at 01:51
0

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.

sn42
  • 2,353
  • 1
  • 15
  • 27
  • Thank you for the lengthy and well thought out reply. I am a little confused however. Is this something that I would want to put into a new class entirely of its own, or multiple new ones. I guess i am just a little confused about the actual implementation of the of the code. Thanks again for all of your help. – Victor Alam Apr 08 '18 at 03:15
  • I think i am figuring it out a bit – Victor Alam Apr 08 '18 at 03:24
  • Update: Still pretty confused getting a lot of errors – Victor Alam Apr 08 '18 at 03:26
  • @Victor Alam My intention was to split my code into classes with [single responsibilities](https://en.m.wikipedia.org/wiki/Single_responsibility_principle) to make the code more robust. If you change for example your strategy of saving your items (e.g. JSON format, database, ...) you only have to look into the `ItemUtils` class (probably a bad name, doesn't describe the classes intend). – sn42 Apr 08 '18 at 03:34
  • @Victor Alam The code leaves out some details. You need to implement the `Item`'s constructor and getters as well as the CustomRuntimeException. Does that help you? – sn42 Apr 08 '18 at 03:41
  • I took a screenshot to show of what I am seeing on my end. I have no idea how to parse the enum itemType type as well. This is what I am seeing on my end. https://imgur.com/a/d1aoU – Victor Alam Apr 08 '18 at 03:44
  • I already did have setters and getters from the past when I originally made the original assignment a bit back do I need to change those. Thank you for all of your help. I think I get what you mean with the custom runtime exception. I am still confused about the error I have with the list on line 20 and how to pass itemtype on line 49 – Victor Alam Apr 08 '18 at 03:45
  • @Victor Alam Import `java.util.List` instead of `java.awt.List` to fix the error in line 20. Regarding the enum: I don't know your implementation of `parseEnum`, hope [this answer](https://stackoverflow.com/a/604426/8089107) helps. Note that you have to change the if condition in line 40 if you have more than 4 tokens. – sn42 Apr 08 '18 at 03:56
  • Thanks again for your help it does help a bit. I do not have any implementation for parseEnum, that is the issue. That is just random stuff that I thought of. I think I am just going to take out the type all together. Do you know how I would make one of those runtimeexceptions. Sorry I am really new to java. Any help would be great. – Victor Alam Apr 08 '18 at 04:03
  • @Victor Alam Create a class extending RuntimeException. Just to be clear: You don't need a custom runtime exception, but it has [some benefits](https://stackoverflow.com/a/48736888/8089107) to using an`IllegalArgumentException`. – sn42 Apr 08 '18 at 04:10
  • I am sorry but I am so confused. You do not have to help anymore if you do not want to. I do not know at all what I would put into that class. And to be honest I am still not sure how this code works. Are all of the different attributes being stored in different sections of "Tokens" – Victor Alam Apr 08 '18 at 04:15
  • @VictorAlam [This answer](https://stackoverflow.com/a/9814378/8089107) is an example how to create a custom exception. The code above uses your approach of storing the attributes of a single item as a comma separated list (`,,,`), splitting it with [String.split](https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#split(java.lang.String)). Read the docs for more info. `readItems` reads all lines of the file to an List, uses Java 8's Stream API to map each line to an item and then collects the items to a List. Does that help you? – sn42 Apr 08 '18 at 04:55
  • Thanks again for your help. This kind of helps me. How would I go about printing a single item from the list. I will work on the error thing right now – Victor Alam Apr 08 '18 at 05:11
  • The `readItems` method returns a `List` which can be iterated with a for each loop: `for (Item item : items) { System.out.println(item.getName()); }` just to make an example. These are basic java questions which can be answered using Google or a textbook, are there any specialized questions regarding my solution? – sn42 Apr 08 '18 at 05:31
  • I think I am okay for now thank you for everything have a nice night. – Victor Alam Apr 08 '18 at 05:34
  • @VictorAlam Sounds good, feel free to accept the answer if you think it solved your issue. – sn42 Apr 08 '18 at 05:40
  • I don't know if you are still following this but I have literally no idea what is going on. I do not even know where to begin. Like what is this program even doing is it collecting the information from each line of my file. And if so how do I even see that information. I do not understand I am really sorry. – Victor Alam Apr 09 '18 at 01:14
  • @VictorAlam I've updated my answer which contains an explanation part. Does this help you? – sn42 Apr 09 '18 at 07:00