How to print only certain sections of a string? - java

First off - please forgive me, for I am an amateur.
For a project, I am taking an excel file as input containing a list of kittens and I want to output the addresses these kittens have been found at.
I have implemented the code so that a kitten is an object with a name, ID, and notes (kitten was assigned these attributes from evaluating each cell in the excel doc). The notes section contains info about where the kitten was found.
Excel document:
name | ID | Notes
--------------------
Kit | 5 | Great animal! Haha! Found at 1234 east
| | street. Incredibly ugly.
---------------------
Kat | 2 | Wow, what a charmer. from location 3456
| | Dusk road
.
.
.
Currently, my program converts the excel doc to a string and prints the entire 'Notes' section for each kitten. I want it to extract the address (trying to get as many of the addresses as possible) from the rest of the string, so the output would look something like this:
1234 east street, 3456 Dusk Road, ...
All I could find online was about String delims, etc, but I am not sure how to start thinking about extracting specific phrases from a long varied string. Is there some way to record info at some key word, like "Found at" or "from location", and then stop at a period?
Would it be easier not to convert each address to one long string, but instead, print out the extracted address for each kitten?
My Code (for reference):
public class Kitten {
private String name;
private String animalID;
private String addressFound;
public Kitten() {
super();
this.name = name;
this.animalID = animalID;
this.addressFound = addressFound;
}
//getters and setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAnimalID() {
return animalID;
}
public void setAnimalID(String animalID) {
this.animalID = animalID;
}
public String getAddress() {
return addressFound;
}
public void setAddress(String addressFound) {
this.addressFound = addressFound;
}
}
INPUT: excel file with kitten info. Prints the "Notes" section for
each kitten
public class ReadExcel {
public void printer() {
try {
FileInputStream kittenFile = new FileInputStream(new
File("./IntakeNotesSimple.xlsx"));
XSSFWorkbook wb = new XSSFWorkbook(kittenFile);
XSSFSheet sheet = wb.getSheetAt(0);
ArrayList<Kitten> kittenList = new ArrayList<>();
for (int i= sheet.getFirstRowNum() + 1; i<= sheet.getLastRowNum();
i++) {
Kitten k = new Kitten();
Row r = sheet.getRow(i);
for (int j = r.getFirstCellNum(); j<= r.getLastCellNum(); j++)
{
Cell c = r.getCell(j);
if (j==0) {
k.setName(c.getStringCellValue());
}
if (j==1) {
k.setAnimalID(c.getStringCellValue());
}
if (j==2 && (c != null)) {
k.setAddress(c.getStringCellValue());
}
}
kittenList.add(k);
}
for (Kitten kit: kittenList) {
System.out.println(kit.getAddress() +"\n" +);
}
wb.close();
}
catch(Exception e) {
e.printStackTrace();
}
}
}
public class PrintOut {
public static void main(String[] args) throws FileNotFoundException {
ReadExcel addresses = new ReadExcel();
addresses.printer();
}
}

Let's say you have a list of words that will give you the start of the address (practically it won't there are so many possibilities, but let's imagine it as you suggest this would work in your OP).
The string you search in will start with some characters, then either "found at" or "from location" and it will end at the next , . ! or ? character. Finally some last part will contain some other characters.
The solution you should be using here is Regex, aka best pattern matching tool you could find. The regex for the pattern described above would be :
^.*?(found at|from location) (.*?)([\.,!?].*+|)$
This regex is not that easy so we might not get into details, I'd better link you to some visual tool for this regex : https://regex101.com/r/q1w428/1
So now, how to use it within java app ?
private static final String KITTEN_PATTERN_STRING = "^.*?(found at|from location) (.*?)([\\.,!?].*+|)$";
private static final Pattern KITTEN_PATTERN = Pattern.compile(KITTEN_PATTERN_STRING);
public String extractKittenAddress(String kittenString) {
Matcher m = KITTEN_PATTERN.matcher(kittenString);
if(m.matches())
return m.group(2);
return null;
}
And there you go!

I assumed that you have a string contains some text and address. And your delimiter word is :
Found at
So you can split your text and extract the address while iterating your data as below :
public class Main {
public static void main(String[]args) throws JsonProcessingException {
String textContaintsAddress = "Great animal! Haha! Found at 1234 east street. Incredibly ugly.";
String address[] = textContaintsAddress.split("Found at");
if (address.length > 1) {
System.out.println(address[1].trim());
}else{
System.out.println(textContaintsAddress);;
}
}
}
It prints :
1234 east street. Incredibly ugly.
Edit your code as below :
public class ReadExcel {
public void printer() {
try {
FileInputStream kittenFile = new FileInputStream(new
File("./IntakeNotesSimple.xlsx"));
XSSFWorkbook wb = new XSSFWorkbook(kittenFile);
XSSFSheet sheet = wb.getSheetAt(0);
ArrayList<Kitten> kittenList = new ArrayList<>();
for (int i= sheet.getFirstRowNum() + 1; i<= sheet.getLastRowNum();
i++) {
Kitten k = new Kitten();
Row r = sheet.getRow(i);
for (int j = r.getFirstCellNum(); j<= r.getLastCellNum(); j++)
{
Cell c = r.getCell(j);
if (j==0) {
k.setName(c.getStringCellValue());
}
if (j==1) {
k.setAnimalID(c.getStringCellValue());
}
if (j==2 && (c != null)) {
// here we add the logic
String textContaintsAddress = c.getStringCellValue();
String address[] = textContaintsAddress.split("Found at");
if (address.length > 1) {
k.setAddress(address[1].trim());
}else{
k.setAddress(textContaintsAddress);;
}
}
}
kittenList.add(k);
}
for (Kitten kit: kittenList) {
System.out.println(kit.getAddress() +"\n" +);
}
wb.close();
}
catch(Exception e) {
e.printStackTrace();
}
}
}

Related

How to format file strings to a dot path

I want to make a configuration to store items, however, when I was making the paths to get the values, something wrong happened.
HashMap<String, Text> sections;
private void loadKeys() {
List<String> list = new ArrayList<>();
for (String s : sections.keySet()) {
Text te = sections.get(s);
String changeable = s.substring(0, s.length() - 1);
for (int i = 0; i < te.lines(); i++) {
String line = te.getLine(i);
while (line.startsWith(" ")) {
line = line.substring(2);
}
if (!line.startsWith("-")) {
if (line.endsWith(":")) {
changeable = changeable + "." + line.substring(0, line.length() - 1);
} else {
list.add(changeable + "." + line);
}
}
}
}
for (String s : list) {
System.out.println(s);
}
}
Text.java
public class Text {
private List<String> lines = new ArrayList<>();
public Text(String txt) {
if (txt.contains("\n")) {
for (String s : txt.split("\n")) {
lines.add(s);
}
} else {
lines.add(txt);
}
}
public int lines() {
return lines.size();
}
public String getLine(int line) {
return lines.get(line);
}
#Override
public String toString() {
String string = "";
for (String s : lines) {
if (string.equals("")) {
string = s;
} else {
string = string + "\n" + s;
}
}
return string;
}
}
File:
Test11:
Test12:
Test13: 'test'
Test14: 'test2'
Test15: teste
Test16:
Test17: "test test"
The output I want:
Test11.Test12.Test13: 'test'
Test11.Test12.Test14: 'test2'
Test11.Test15: teste
Test11.Test16.Test17: "test test"
What I got with the code above:
Test11.Test12.Test13: 'test'
Test11.Test12.Test14: 'test2'
Test11.Test12.Test15: teste
Test11.Test12.Test16.Test17: "test test"
Test12 is being repeated. Can you help me have what I want? Thanks in advance
It is pretty easy. All you need is just keep current level depth and level name. You can do it via recursion or using queue.
public static Map<String, String> readProperties(Path path) throws IOException {
final class Level {
private final String name;
private final int offs;
public Level(String name, int offs) {
this.name = name;
this.offs = offs;
}
}
Map<String, String> map = new LinkedHashMap<>();
// contains all root items for current one with it's offset, to detecl that current level is sub level or parent
Deque<Level> levels = new LinkedList<>();
Pattern pattern = Pattern.compile("(?<offs>\\s*)(?<key>[^:]+)\\s*:\\s*(?<value>.*)\\s*");
Files.lines(path)
.map(pattern::matcher)
.filter(Matcher::matches)
.forEach(matcher -> {
int offs = matcher.group("offs").length();
// remove parent levels until reach the parent of current level
while (!levels.isEmpty() && levels.peekLast().offs >= offs) {
levels.removeLast();
}
String key = matcher.group("key");
String value = matcher.group("value");
if (value.isEmpty())
levels.add(new Level(key, offs));
else
map.put(levels.stream().map(level -> level.name).collect(Collectors.joining(".")) + '.' + key, value);
});
return map;
}

ArrayList overwriting

I have an ArrayList(called filmes) in which one of the elements is another ArrayList(called actores), this actores is made out of names of actors that have the same movie id as the movie in filmes.
But every time it adds an actor to the actores ArrayList it overwrites the previous one there.
So when there's more than one actor for the same movie it just shows the last one on the file.
For example the expected output should be 6978 | Big Trouble in Little China | 30-05-1986 [Kurt Russel,Kim Cattrall] [Comedy] but instead it's printing 6978 | Big Trouble in Little China | 30-05-1986 [Kim Cattrall] [Comedy]
/*Actorsfile.txt (the last element is the movie id)
11701,Angelina Jolie,false,1995
6384,Keanu Reeves,true,603
7382,Carrie-Anne Moss,false,603
11701,Angelina Jolie,false,10428
6856,Kurt Russell,true,6978
2109,Kim Cattrall,false,6978*/
try{
File ficheiroA = new File(Actorsfile);
Scanner leitorFicheiroA = new Scanner(ficheiroA);
while (leitorFicheiroA.hasNextLine()) {
ArrayList<Actor> actores = new ArrayList<>();
Actor actor = new Actor("");
String linha = leitorFicheiroA.nextLine();
String dadosA[] = linha.split(",");
if (dadosA.length == 4) {
int idFilmeA = Integer.parseInt(dadosA[3]);
int pos = index(idFilmeA, ids);
if (idFilmeA == ids[pos]) {
actor.nome = (dadosA[1]);
actores.add(actor);
filmes.get(pos).mudarActores(actores);
}
}
}
}
catch(FileNotFoundException e) {
String mensagem = "Erro: o ficheiro " + ficheiroActores + " nao foi encontrado.";
System.out.println(mensagem);
}
Actor Class:
public class Actor {
String nome;
String genero;
public Actor(String nome) {
this.nome = nome;
}
public String toString () {
return nome;
}
}
Filme Class:
public class Filme {
int id;
String titulo;
ArrayList<Actor> actores= new ArrayList<>();
ArrayList<GeneroCinematografico> generos= new ArrayList<>();
String dia,mes,ano;
public Filme(int id, String titulo,ArrayList<Actor> actores, ArrayList<GeneroCinematografico> generos,String ano,String mes,String dia) {
this.id = id;
this.titulo = titulo;
this.actores = actores;
this.generos = generos;
this.ano=ano;
this.mes=mes;
this.dia=dia;
}
public void mudarId(int id) {
this.id = id;
}
public void mudarTitulo(String titulo) {
this.titulo = titulo;
}
public void mudarActores(ArrayList<Actor> actores) {
this.actores =actores;
}
public void mudarGeneros(ArrayList<GeneroCinematografico> generos) {
this.generos = generos;
}
public String toString() {
return id + " | " + titulo + " | " + dia + "-" + mes + "-" + ano +" "+ actores +" "+ generos;
}
}
Edit:
I've done it a different way and now it's working well in the first filme, but the next are as a null.
Recent output it's 603 | The Matrix | 30-03-1999 [Keanu Reeves, Carrie-Anne Moss] [Science Fiction], 10428 | Hackers | 14-09-1995 null [Action] but the expected is 603 | The Matrix | 30-03-1999 [Keanu Reeves, Carrie-Anne Moss] [Science Fiction], 10428 | Hackers | 14-09-1995 [Angelina Jolie] [Action]. I would be greatful if I could do it this way, without using maps.
try{
File ficheiroA = new File(ficheiroActores);
Scanner leitorFicheiroA = new Scanner(ficheiroA);
i=0;
while (i<ids.length-1) {
int idFilme=ids[i];
ArrayList<Actor> actores = new ArrayList<>();
while (leitorFicheiroA.hasNextLine()) {
Actor actor;
String linha = leitorFicheiroA.nextLine();
String dadosA[] = linha.split(",");
int idFilmeA = Integer.parseInt(dadosA[3]);
//int pos = index(idFilmeA, ids);
if (dadosA.length == 4) {
if (idFilmeA == idFilme) {
actor =new Actor (dadosA[1]);
actores.add(actor);
}
}
filmes.get(i).mudarActores(actores);
}
i++;
}
You are declaring a new ArrayList inside the while, so it is resetting each iteration. What you want to do is to take the creation of the ArrayList outside the while. Instead of:
while (leitorFicheiroA.hasNextLine()) {
ArrayList<Actor> actores = new ArrayList<>();
You want:
ArrayList<Actor> actores = new ArrayList<>();
while (leitorFicheiroA.hasNextLine()) {
And then you call filmes.get(pos).mudarActores(actores); when the while finishes.
Edit:
To solve the problem you are commenting, I would remove filmes.get(pos).mudarActores(actores); from where it is now. Note that you have all the information you need in each Actor. So,I would leave the logic of the current while as is now, maybe put it in a function readActoresFromFile(), returning the List of Actores. Then I would iterate over each element of the List and put them on a Map which has the filmId as Key and the list of Actors of that film as values. Tell me if you need help with that.

antlr4/java: pretty print parse tree to stdout

Beginners question: how do I print a readable version of the parse tree to stdout?
CharStream input = CharStreams.fromFileName("testdata/test.txt");
MyLexer lexer = new MyLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
MyParser parser = new MyParser(tokens);
parser.setBuildParseTree(true);
RuleContext tree = parser.record();
System.out.println(tree.toStringTree(parser));
this prints the whole tree on a single line delimited by brackets '()'.
(record (husband <4601> (name KOHAI Nikolaus) \n (birth * um.1872 (place Ploschitz)) \n\n) (wife (marriage oo) \n (name SCHLOTTHAUER Maria) \n (birth * um.1877
...
I would like to have something like this
record
husband
<id>
name
<name>
...
wife
Extracted from SnippetsTest as a standalone utility class:
import java.util.List;
import org.antlr.v4.runtime.misc.Utils;
import org.antlr.v4.runtime.tree.Tree;
import org.antlr.v4.runtime.tree.Trees;
public class TreeUtils {
/** Platform dependent end-of-line marker */
public static final String Eol = System.lineSeparator();
/** The literal indent char(s) used for pretty-printing */
public static final String Indents = " ";
private static int level;
private TreeUtils() {}
/**
* Pretty print out a whole tree. {#link #getNodeText} is used on the node payloads to get the text
* for the nodes. (Derived from Trees.toStringTree(....))
*/
public static String toPrettyTree(final Tree t, final List<String> ruleNames) {
level = 0;
return process(t, ruleNames).replaceAll("(?m)^\\s+$", "").replaceAll("\\r?\\n\\r?\\n", Eol);
}
private static String process(final Tree t, final List<String> ruleNames) {
if (t.getChildCount() == 0) return Utils.escapeWhitespace(Trees.getNodeText(t, ruleNames), false);
StringBuilder sb = new StringBuilder();
sb.append(lead(level));
level++;
String s = Utils.escapeWhitespace(Trees.getNodeText(t, ruleNames), false);
sb.append(s + ' ');
for (int i = 0; i < t.getChildCount(); i++) {
sb.append(process(t.getChild(i), ruleNames));
}
level--;
sb.append(lead(level));
return sb.toString();
}
private static String lead(int level) {
StringBuilder sb = new StringBuilder();
if (level > 0) {
sb.append(Eol);
for (int cnt = 0; cnt < level; cnt++) {
sb.append(Indents);
}
}
return sb.toString();
}
}
Call the method as follows:
List<String> ruleNamesList = Arrays.asList(parser.getRuleNames());
String prettyTree = TreeUtils.toPrettyTree(tree, ruleNamesList);
Besides a graphical parse tree my ANTLR4 extension for Visual Studio Code also produces a formatted text parse tree:
If you like to use regex only for what it's really for, you can always print a tree by yourself:
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.Trees;
public static String printSyntaxTree(Parser parser, ParseTree root) {
StringBuilder buf = new StringBuilder();
recursive(root, buf, 0, Arrays.asList(parser.getRuleNames()));
return buf.toString();
}
private static void recursive(ParseTree aRoot, StringBuilder buf, int offset, List<String> ruleNames) {
for (int i = 0; i < offset; i++) {
buf.append(" ");
}
buf.append(Trees.getNodeText(aRoot, ruleNames)).append("\n");
if (aRoot instanceof ParserRuleContext) {
ParserRuleContext prc = (ParserRuleContext) aRoot;
if (prc.children != null) {
for (ParseTree child : prc.children) {
recursive(child, buf, offset + 1, ruleNames);
}
}
}
}
Usage:
ParseTree root = parser.yourOwnRule();
System.out.println(printSyntaxTree(parser, root));
I wanted to put in my own spin on this, taking advantage of the fact that I already use StringTemplate in my project. This means I don't have to manually deal with levels like the other answers. It also makes the output format easier to customize.
On top of that, the main reason I'm posting this is because I decided to skip printing rules that I'm only 'passing through', i.e. when using chain rules
a : b | something_else ;
b : c | another ;
c : d | yet_more ;
d : rule that matters ;
since they cluttered my output when checking trees from small inputs without adding any usefull information. This is also easy to change, at the //pass-through rules comment location.
I also copied in the definition of Trees.getNodeText and modified it to use a plain array to get rid of the unnecessary wrapping, and even let me customize it if I feel like it.
Finally, I made it take the parser and tree and just straight dump to System.out, since that's the only situation I need it in.
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.Tree;
import org.stringtemplate.v4.ST;
//for pretty-dumping trees in short form
public class TreeUtils {
private static final ST template() {
return new ST("<rule_text>\n\t<child; separator=\"\n\">");
}
private static final ST literal(String text) {
return new ST("<text>").add("text", text);
}
public static void dump(Parser parser, Tree tree) {
System.out.println(process(parser.getRuleNames(),tree).render());
}
private static String getNodeText(Tree t, String[] ruleNames) {
if ( t instanceof RuleContext ) {
int ruleIndex = ((RuleContext)t).getRuleContext().getRuleIndex();
String ruleName = ruleNames[ruleIndex];
return ruleName;
}
else if ( t instanceof ErrorNode) {
return t.toString();
}
else if ( t instanceof TerminalNode) {
Token symbol = ((TerminalNode)t).getSymbol();
if (symbol != null) {
String s = symbol.getText();
return s;
}
}
Object payload = t.getPayload();
if ( payload instanceof Token ) {
return ((Token)payload).getText();
}
return t.getPayload().toString();
}
private static ST process(String[] ruleNames, Tree t) {
if(t.getChildCount()==0) {
return literal(getNodeText(t, ruleNames));
} else if(t.getChildCount()==1) {
//pass-through rules
return process(ruleNames,t.getChild(0));
} else {
ST out=template();
out.add("rule_text", getNodeText(t, ruleNames));
for(int i=0;i<t.getChildCount();i++) {
out.add("child", process(ruleNames,t.getChild(i)));
}
return out;
}
}
}
For Kotlin, you can use this extension function
fun Tree.format(parser: Parser, indent: Int = 0): String = buildString {
val tree = this#format
val prefix = " ".repeat(indent)
append(prefix)
append(Trees.getNodeText(tree, parser))
if (tree.childCount != 0) {
append(" (\n")
for (i in 0 until tree.childCount) {
append(tree.getChild(i).format(parser, indent + 1))
append("\n")
}
append(prefix).append(")")
}
}

Need help using an ArrayList

It seems that 20 regiments were in a continuous process of formation. The first had 1000 men, the second had 950, the third 900, and so on down to the twentieth regiment, which garrisoned only 50. During each week, 100 men were added to each regiment, and at week's end, the largest regiment was sent off to the front.This lasted for a total of 20 weeks.
For this program I have already managed to print out the original number of men for each regiment. But I am having difficult adding 100 men to each regiment.The adding men must be a method in the army class. I am getting the regiment objects using a .txt file. All this files contains is the names of regiments numbered 1-20.
I currently have no errors my only problem is that I do not know how to add men to my regiment. I have to use the addMen method in the army class which I currently have blank.
public class Regiment {
private String name; //name of regiment
private int regNumber; //regiment number
private int men; // regiment men
public Regiment(int regNumber, String name, int men) {
this.name = name;
this.regNumber = regNumber;
this.men = men;
}
public String getName() {
return name;
}
public int getregNumber() {
return regNumber;
}
public int getMen() {
return men;
}
public int addMen2(int RegNumber) {
int men = 1050 - (regNumber * 50);
return men;
}
}
ArmyDataList:
class ArmyDataList {
public ArrayList<Regiment> list;
public ArmyDataList() {
list = new ArrayList<Regiment>();
}
public void AddToList(Regiment current) {
list.add(current);
}
public void RemoveFromList(Regiment current) {
list.remove(current);
}
public Regiment getLargest() {
if (list.isEmpty()) {
return null;
}
Regiment Reg1 = list.get(0);
for (int i = 1; i < list.size(); i++) {
Regiment current = list.get(i); // get next regiment
// is current regiment > largest
if (current.getMen() > Reg1.getMen()) {
Reg1 = current;
}
}
return Reg1;
}
public void addMen() {
}
public String toString() {
String out
= String.format("%28s%12s%n", "Regiments", " Men")
+ String.format("%12s%n", "Number")
+ String.format("%12s%16s%14s%n", "=======", "===============",
"=========");
for (int i = 0; i < list.size(); i++) {
Regiment regim = list.get(i);
int regNumber = regim.getregNumber();
String name = regim.getName();
int men = regim.addMen2(regNumber);
out = out + String.format("%12s", regNumber)
+ String.format("%16s", name)
+ String.format("%10s", men)
+ "\n";
}
return out + "\n";
}
}
RegimentTest:
public class RegimentTest {
public static void main(String[] args) throws IOException
{
ArmyDataList army = new ArmyDataList();
Scanner fileScan = new Scanner(new File("regiments.txt"));
System.out.println("Report Summary:\n");
while (fileScan.hasNext()) {
String line = fileScan.nextLine();
System.out.println(line);
Scanner in = new Scanner(line) ;
int regNumber = in.nextInt();
String name = in.next();
int men = 0 ; //men is set to 0 only because I havent add the men yet
Regiment adder = new Regiment(regNumber, name, men );
army.AddToList(adder) ;
}
System.out.println(army.toString());
}
Add a setMen(int numberOfMen) method to your Regiment class. Then in your addMen() method, you can do something like this:
public void addMen(){
for(Regiment r : list){ //iterate through the list of regiments
r.setMen(r.getMen() + 100); //add 100 men to each regiment
}
}
The setMen method would look like this:
public void setMen(int numberOfMen){
men = numberOfMen;
}
There is another issue with your toString method, where the regiment's addMen2 method is called - right now you're just printing the number, not initializing the number of men. In the constructor for your Regiment class, replace the line
this.men = men;
with
this.men = addMen2(regNumber);
Then in your toString method, replace
int men = regim.addMen2(regNumber);
with
int men = regim.getMen();
Here is what your main should look like:
public static void main(String[] args) throws IOException{
ArmyDataList army = new ArmyDataList();
Scanner fileScan = new Scanner(new File("regiments.txt"));
System.out.println("Report Summary:\n");
while (fileScan.hasNext()) {
String line = fileScan.nextLine();
System.out.println(line);
Scanner in = new Scanner(line);
int regNumber = in.nextInt();
String name = in.next();
int men = 0 ; //men is set to 0 only because I havent add the men yet
Regiment adder = new Regiment(regNumber, name, men );
army.AddToList(adder);
}
System.out.println(army.toString()); //print out the initial # of men
for(int i = 0; i < 20; i++)
army.addMen();
System.out.println(army.toString()); //print the final # of men
}
in Regiment get rid of method addMen2, and replace it with
public void addMen(int men) {
this.men +=men;
}
then in your army you could have method
public void addMen(int men) {
for(Regiment regiment : list){
regiment.addMen(men);
}
}
that will be simplest solution to add 100 men to each regiment,
other thing is, your toString is bit nasty, regiment should know how meny soldiers it ghas, you shouldnt need additional method to calculate it (reason why i recommend you to trash addMen2 method)
to initiate your Regiment, use constructor. You want to have regiments in sizes 1000, 1950, 1900 etc, do it when you are creating them
while (fileScan.hasNext()) {
String line = fileScan.nextLine();
System.out.println(line);
Scanner in = new Scanner(line) ;
int regNumber = in.nextInt();
String name = in.next();
int men = 1050 - (regNumber * 50);
Regiment adder = new Regiment(regNumber, name, men );
army.AddToList(adder) ;
}

Cannot read text file into array and write to text file again

This my whole code.
I want to make a simple program that will read a
text file and put it to array then write it to the
same text file,
also can add and delete the existing input and my input.
Problem
The delete and writer part seems not working, only blank text file when I run the code
These are the error after I select the exit.
java.lang.NullPointerException at ContactList.writer(ContactList.java:51) at
ContactListDriver.main(ContactListDriver.java:73) at
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at
java.lang.reflect.Method.invoke(Unknown Source) at
edu.rice.cs.drjava.model.compiler.JavacCompiler.runCommand(JavacCompiler.java:27‌​2)
public class Contact {
//Each contact stores the name, phone number, and email address
private String name;
private String number;
private String email;
public Contact(String name, String number, String email)
{
this.name = name;
this.number = number;
this.email = email;
}
public String getName()
{
return name;
}
public String getNumber()
{
return number;
}
public String getEmail()
{
return email;
}
public void setName(String name)
{
this.name = name;
}
public void setNumber(String number)
{
this.number = number;
}
public void setEmail(String email)
{
this.email = email;
}
}
class for processing the inputs.
import java.io.*;
import java.lang.*;
import java.util.*;
public class ContactList {
public Contact[] myContacts;
public static final int MAX = 100;
private int numContacts;
public ContactList()
{
myContacts = new Contact[MAX];
numContacts = 0;
}
public void addContact(String name, String number, String email)
{
Contact c = new Contact(name, number, email);
myContacts[numContacts] = c;
numContacts++;
}
public void deleteContact(String name)
{
for ( int i = 0; i <= numContacts-1 ; i++){
if( name == myContacts[i].getName())
{
myContacts[i] = null;
break;
}
}
numContacts--;
}
public void writer(){
String x = "MyContacts.txt";
try {
PrintWriter outputs = new PrintWriter(x);
for( int i=0; i < myContacts.length; i++)
{
Contact c = myContacts[i];
if(c!=null){ // check if c is null before writing to file
outputs.println(""+c.getName()+" "+c.getNumber()+" "+c.getName());
outputs.flush();
}
}
outputs.close();
}catch (IOException e) {
e.printStackTrace();
}
catch(NullPointerException ex){
}
}
public void displayContacts()
{
int i;
for(i=0; i < myContacts.length; i++)
{
Contact c = myContacts[i];
if(null != c){
System.out.println("Name: " + c.getName());
System.out.println("Number: " + c.getNumber());
System.out.println("Email: " + c.getEmail());
System.out.println("------------------------------------");
}
}
}
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
The Driver....
public class ContactListDriver {
public static void main(String[] args) throws FileNotFoundException
{
ContactList cList = new ContactList();
File in = new File("MyContacts.txt");
Scanner sc = new Scanner(in);
int option;
char again = 'n';
String name = null;
String number = null;
String email = null;
while(sc.hasNext())
{
//read one line from text file
String entry = sc.nextLine();
//System.out.println(entry);
String[] con = entry.split("\\s+");
//System.out.println(con[0] + " " + con[1] + " " + con[2]);
cList.addContact(con[0], con[1], con[2]);
}
Scanner userIn = new Scanner(System.in);
do{
displayOptions();
option = userIn.nextInt();
switch(option)
{
case 1:
System.out.println(" Name > ");
name = userIn.next();
System.out.println(" Number > ");
number = userIn.next();
System.out.println(" Email Address > ");
email = userIn.next();
cList.addContact(name, number, email);
break;
case 2:
//delete contact
System.out.println("Contact Name > ");
name = userIn.next();
cList.deleteContact(name);
break;
case 3:
//display contact
cList.displayContacts();
break;
case 4:
cList.writer();
System.out.println(" are you sure ? press y ");
String x = userIn.next();
again = x.charAt(0);
break;
}
}while( again == 'n' );
}
private static void displayOptions() {
System.out.println("(1) Add");
System.out.println("(2) Delete");
System.out.println("(3) Show Contacts");
System.out.println("(4) Exit");
}
}
One problem I see is:
You have a extra break; statement inside deleteContact(String name) function
and String comparision name == myContacts[i].getName() should be name.equals(myContacts[i].getName())
public void deleteContact(String name)
{
for ( int i = 0; i <= numContacts-1; i++){
if( name.equals( myContacts[i].getName()))// string comparison uses equals();
{
myContacts[i] = null;
numContacts--; // this line should be inside of if condition
break;
}
// break; No need of breaking the loop here
}
}
Another problem is at writer() function
public void writer(){
String x = "MyContacts.txt";
try {
PrintWriter outputs = new PrintWriter(x);
for( int i=0; i < myContacts.length; i++)
{
Contact c = myContacts[i];
if(c!=null){ // check if c is null before writing to file
outputs.println(""+c.getName()+" "+c.getNumber()+" "+c.getName());
outputs.flush();
}
}
outputs.close();
}catch (IOException e) {
e.printStackTrace();
}
catch(NullPointerException ex){ // Or just catch the NPE
}
You have declared and initialized the Contact array of size MAX. but,it seems to be that you haven't initialized the elements though. i.e. c is null in the below code
Contact c = myContacts[i];
outputs.println(""+c.getName()+" "+c.getNumber()+" "+c.getName());
outputs.flush();
myContacts[i] should return a Contact instance. As said by Meno, there are lot of other problems in your code. You have to always cover all the possible scenarios while writing the code.
Most importantly you need to fix the ContactList class. It is inserting new elements into the last index, and deleting at any location using the name.
For example, let's say the ContactList has three elements in it at 0, 1 and 2 indexes. So numContacts is set to 3.
Now ContactList has elements as:
[0]C0, [1]C1, [2]C2, [3]null, ...
Then if the contact at 0 index is deleted (set to null), then numContacts is set to 2.
Now the ContactList has elements as:
[0]null, [1]C1, [2]C2, [3] null, ...
A new insert will be added to the index 2, and it will override the C2 value.
Simplest solution is to use an ArrayList instead of an array.
As others have mentioned there are few more issues to fix, but above is the most important in my opinion.
There are many issues with your code so not easy to say where to begin.
First: Your public void deleteContact(String name)-method is broken. It compares Strings using == instead of equals(). And worse: It creates null pointers mid in your array which will cause problems in your writer()-method.
Second: Why do you use arrays? You should use java.util.ArrayList which offers out-of-the-box implementations for adding, getting and deleting contacts.
Third: If you are missing your text file, you might have overlooked it because of missing path so you don't know where to look for this file. So please add a full path to file name.
Fourth: I would also use scanner.hasNextLine() instead of scanner.hasNext() if you then call scanner.nextLine().
Since you said you are not allowed to use ArrayList you should study its source code especially for removing elements. It does not only nullify the array bucket, but also to shift all following elements one index backwards so you don't have any null gap until the index given by element count. And two breaks in deleteContact()-method are really not necessary.

Categories