-2

I am learning how to work with files in Java. I have a sample file which contains key pairs and it values. I am trying to find a key pairs and if it matches, then output file would be updated with both, key pair and it's value. I am able to get key pairs in output file but unable to get values too. Stringbuilder may work here to append strings but I don't know how.

Below are my input and output files.

Input File:

born time 9 AM London -- kingNumber 1234567890 -- address: abc/cd/ef -- birthmonth: unknown
born time 9 AM Europe -- kingNumber 1234567890 -- address: abc/cd/ef -- birthmonth: december

Expected Output File:

kingNumber 1234567890 birthmonth unknown 
kingNumber 1234567890 birthmonth unkbown

Current Output File:

kingNumber birthmonth 
kingNumber birthmonth

I am able to write key pair ("kingNumber" and "birthmonth" in this case) to output file but I am not sure what I can do to get it's value too.

    String kn = "kingNumber:";
    String bd = "birthmonth:";

    try {

        File f = new File("sample.txt");
        Scanner sc = new Scanner(f);
        FileWriter fw = new FileWriter("output.txt");


        while(sc.hasNextLine()) {
            String lineContains = sc.next();
            if(lineContains.contains(kn)) {
                fw.write(kn  + "\n");
                // This is where I am stuck. What
                // can I do to get it's value (number in this case).
            }
            else if(lineContains.contains(bd)) {
                fw.write(bd);
                // This is where I am stuck. What
                // can I do to get it's value (birthday in this case).
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
Andre
  • 19
  • 9
  • Not exactly sure what your problem is, but you may want to use .nextLine() instead of .next(). – Jacob B. Aug 18 '18 at 23:50
  • @JacobB. I updated my question, maybe it would be more clear now. I am trying to get those missing values. – Andre Aug 19 '18 at 00:35
  • 1
    You should take a look at string format (regex) and delimiters to extract the data from your string – Fundhor Aug 19 '18 at 00:39
  • Possible duplicate of [Using Java to find substring of a bigger string using Regular Expression](https://stackoverflow.com/questions/600733/using-java-to-find-substring-of-a-bigger-string-using-regular-expression) – Martin Zeitler Aug 19 '18 at 01:11

2 Answers2

0

I have written a simple parser that it following data format from your example. You will need to call it like this:

PairParser parser = new PairParser(lineContains);

then you can get value from the parser by pair keys How to get value:

parser.getValue("kingNumber")

Note that keys do not have trailing column character.

The parser code is here:

package com.grenader.example;

import java.util.HashMap;
import java.util.Map;

public class PairParser {

    private Map<String, String> data = new HashMap<>();

    /**
     * Constructor, prepare the data
     * @param dataString line from the given data file
     */
    public PairParser(String dataString) {
        if (dataString == null || dataString.isEmpty())
            throw new IllegalArgumentException("Data line cannot be empty");

        // Spit the input line into array of string blocks based on '--' as a separator
        String[] blocks = dataString.split("--");

        for (String block : blocks)
        {
            if (block.startsWith("born time")) // skip this one because it doesn't looks like a key/value pair
                continue;
            String[] strings = block.split("\\s");
            if (strings.length != 3) // has not exactly 3 items (first items is empty), skipping this one as well
                continue;
            String key = strings[1];
            String value = strings[2];
            if (key.endsWith(":"))
                key = key.substring(0, key.length()-1).trim();

            data.put(key.trim(), value.trim());
        }
    }

    /**
     * Return value based on key
     * @param key
     * @return
     */
    public String getValue(String key)
    {
        return data.get(key);
    }

    /**
     * Return number of key/value pairs
     * @return
     */
    public int size()
    {
        return data.size();
    }
}

And here is the Unit Test to make sure that the code works

package com.grenader.example;

import com.grenader.example.PairParser;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

public class PairParserTest {

    @Test
    public void getValue_Ok() {
        PairParser parser = new PairParser("born time 9 AM London -- kingNumber 1234567890 -- address: abc/cd/ef -- birthmonth: unknown");

        assertEquals("1234567890", parser.getValue("kingNumber"));
        assertEquals("unknown", parser.getValue("birthmonth"));
    }

    @Test(expected = IllegalArgumentException.class)
    public void getValue_Null() {
        new PairParser(null);
        fail("This test should fail with Exception");
    }

    @Test(expected = IllegalArgumentException.class)
    public void getValue_EmptyLine() {
        new PairParser("");
        fail("This test should fail with Exception");
    }

    @Test()
    public void getValue_BadData() {
        PairParser parser = new PairParser("bad data bad data");
        assertEquals(0, parser.size());

    }
}
Zabuzard
  • 25,064
  • 8
  • 58
  • 82
Igor Kanshyn
  • 867
  • 6
  • 13
  • I understand what you are trying to do but I am kind of confused with input for constructor. Here the input is whole text file and not just one word, so I am not sure how can it be changed. – Andre Aug 19 '18 at 17:36
  • The constructor needs one line of text. This line in your code: String lineContains = sc.next(); have the required line in lineContains variable. You can create the Parser just after this line. – Igor Kanshyn Aug 19 '18 at 17:58
  • That's strange. I am trying to do what you said but some reason it isn't working. – Andre Aug 19 '18 at 22:02
  • What is not working? :) Maybe try to System.out.println() some values or debug your application. – Igor Kanshyn Aug 20 '18 at 16:08
0

you could use java.util.regex.Pattern & java.util.regex.Matcherwith a pattern alike:

^born\stime\s([a-zA-Z0-9\s]*)\s--\skingNumber\s(\d+)\s--\saddress:\s([a-zA-Z0-9\s/]*)\s--\sbirthmonth:\s([a-zA-Z0-9\s]*)$

write less, do more.

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • What exactly are you doing in this? Can you explain? – Andre Aug 19 '18 at 02:06
  • this should provide an idea of the how & why: https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285 eg. the above pattern takes 70 steps to match all 4 capture groups (the less steps, the quicker it is; often one can optimize them); also see the answer which I've linked above. – Martin Zeitler Aug 19 '18 at 02:18