reading student record from file - java

I'm trying to read a file that has student record(first name, last name, and grade).
I have written a simple code to accomplish this task but the code fails after reading two lines from the text file. Here is my code:
public class Student {
private final String first,last;
final int MAXGRADE = 100;
final int LOWGRADE = 0;
private final int grade;
public Student(String firstname,String lastname, int grade){
this.first = firstname;
this.last = lastname;
this.grade = grade;
}
#Override
public String toString(){
return first + " " + last + "\t" + grade;
}
}
and the driver has this code
public class driver {
public static void main(String[] args) throws FileNotFoundException {
String first_name ,last_name;
int grade;
Scanner fileInput = new Scanner(new File("data1.txt"));
while (fileInput.hasNextLine())
{
first_name = fileInput.next();
last_name = fileInput.next();
grade = fileInput.nextInt();
Student st = new Student(first_name, last_name,grade);
System.out.println(st);
}
}
}
the compiler is pointing to this
grade = fileInput.nextInt();
as the source of the error.

This code is working for me. Make Sure
The location of text file correctly given,
Integer value given at 3rd position in each line (like:- steve smith 22)

If you are using Java 8, then functional way of doing this would be:
String filePath = "C:/downloads/stud_records.txt"; // your file path
/*
* Gives you a list of all students form the file
*/
List<Student> allStudentsFromFile = Files.lines(Paths.get(filePath)).map(line -> {
String[] data = line.split("\\s+"); //Split on your delimiter
Student stud = new Student(data[0], data[1], Integer.parseInt(data[2]));
return stud;
}).collect(Collectors.toList());
Note: I've made an assumption that this:
FirstName LastName Grade
is the input file format.

From the comment you post "#AxelH each line represents a single student first, last name and grade" we can see the problem.
Your actual loop to read a line
while (fileInput.hasNextLine())
{
first_name = fileInput.next();
last_name = fileInput.next();
grade = fileInput.nextInt();
Student st = new Student(first_name, last_name,grade);
System.out.println(st);
}
Is reading 3 lines, one per fileInput.nextXXX();. What you need to do is
Read a line as a String : `String line = fileInput.nextLine();
Split that line base on the delimiter : `String[] data = line.split(" "); //Or your delimiter if not space (carefull with some names...)
Set the value from this array (parse are need for integer)
EDIT :
I have made a mistake since I am used to use nextline and not next, I can't delete the answer as it is accepted so I will update it to be more correct without changing the content.
The code is indeed correct, next will take the following input until the next delimiter, \\p{}javaWhitespace}+, but using the given solution would give you more solution to manage composed names as it could be Katrina Del Rio 3.

Related

Java - how to extract multiple things from a string and store in an object

public class Main{
public static void main(String[] args)
{
String a = "PORT:AXN,0,10;BGT,20,30;CXZ,10,30|BENCH:AXN,50,10;BGT,30,30;DFG,30,20;XYZ,0,10";
Port port = new Port();
Port bench = new Port();
}
}
public class Port{
List<String> name = new ArrayList<String>();
List<Integer> qty = new ArrayList<Integer>();
List<Integer> price = new ArrayList<Integer>();
}
I want to go through the string and store the information in these objects so
port object gets:
AXN,BGT,CXZ in the name list
0,20,10 in the qty list
10,30,30 in the price list
And similarly for the bench object using the bench data in the string.
In the string, there can be any number of tuples in the string for each. eg. port can have 5 sets, bench can have 8. And the codes for each (the name) have to be 3 alpha characters long but can be anything.
How can I go about doing this? The only thing I can think of is using the split method somehow but am having a hard time working out exactly how to use it? Any help would be great! thanks
You can use split with some loops like this :
String a ="PORT:AXN,0,10;BGT,20,30;CXZ,10,30|BENCH:AXN,50,10;BGT,30,30;DFG,30,20;XYZ,0,10";
Port port = new Port();
String[] tuples = a.split("\\|");
//Split with | to get the tuples
//PORT:AXN,0,10;BGT,20,30;CXZ,10,30
//BENCH:AXN,50,10;BGT,30,30;DFG,30,20;XYZ,0,10
for(String tuple : tuples){
String[] objects = tuple.split(";");//split each tuple with ; to get the information
//PORT:AXN,0,10
//BGT,20,30
//CXZ,10,30
for(String obj : objects){
//split with , to get the three properties(name, qty, price)
String[] spl = obj.replaceAll("(.*?):(.*?)", "$2").split(",");
port.name.add(spl[0]);//name value
port.qty.add(Integer.parseInt(spl[1]));//qty value
port.price.add(Integer.parseInt(spl[2]));//price value
}
}
Classes
As I suggested, I think modifying a bit your Port class and creating a PortObject class could improve readibility. Basically:
A port has a name and a list of port objects
A port object has a name, a quantity and a price:
For each class, I'll create two constructor:
constructor which takes all attributes and assign
constructor which build object from a raw String input
Classes are private static and don't have getter because I put everything in the Main.java but this is not a good practice!
Port class
private static class Port {
final String name;
final List<PortObject> portObjects;
// format is PORT:AXN,0,10;BGT,20,30;CXZ,10,30
public Port(String input) {
String[] split = input.split(":");
name = split[0];
this.portObjects = Pattern.compile(";").splitAsStream(split[1])
.map(PortObject::new)
.collect(Collectors.toList());
}
public Port(String name, List<PortObject> portObjects) {
this.name = name;
this.portObjects = portObjects;
}
#Override
public String toString() {
return "Port:" + name + " " + portObjects.toString();
}
}
Port object class
private static class PortObject {
final String name;
final int qty;
final int price;
// format is AXN,0,10
public PortObject(String input) {
String[] split = input.split(",");
name = split[0];
qty = Integer.parseInt(split[1]);
price = Integer.parseInt(split[2]);
}
public PortObject(String name, int qty, int price) {
this.name = name;
this.qty = qty;
this.price = price;
}
#Override
public String toString() {
return name + ":" + qty + ":" + price;
}
}
Not a so good idea: Regex
When it's about matching a pattern, one may think about Regex. Regex will require the second constructor (the one which does not take a raw String input as argument). I'm not a Regex expert so I'll do a simple way:
[^\\|]+ is the equivalent of String.split("\\|")
(\\w+):(.*) will match the port name and the list of port objects. This list will be processed by the next regex:
(\\w{3}),(\\d+),(\\d+);? will match the three-characters port object name, ask for a digit-only quantity and a digit-only price followed by an optional semi-colon (optional because the last port object does not have semi-colon)
Put in code, it looks like as follow:
public static void main(String[] args) throws IOException {
// the input
String a = "PORT:AXN,0,10;BGT,20,30;CXZ,10,30|BENCH:AXN,50,10;BGT,30,30;DFG,30,20;XYZ,0,10";
// Option 1: Regex
List<Port> portList1 = new ArrayList<>();
// initialise regex
String splitRegex = "[^\\|]+"; // Regex 1.
String portRegex = "(\\w+):(.*)"; // Regex 2.
String portObjectRegex = "(\\w{3}),(\\d+),(\\d+);?"; // Regex 3.
Pattern patternSplit = Pattern.compile(splitRegex);
Pattern patternPort = Pattern.compile(portRegex);
Pattern patternPortObject = Pattern.compile(portObjectRegex);
Matcher matcherSplit = patternSplit.matcher(a);
Matcher matcherPort;
Matcher matcherPortObject;
// look for each port
while (matcherSplit.find()) {
String portSplit = matcherSplit.group();
matcherPort = patternPort.matcher(portSplit);
while (matcherPort.find()) {
// keep the port name
String name = matcherPort.group(1);
List<PortObject> portObjectList = new ArrayList<>();
matcherPortObject = patternPortObject.matcher(matcherPort.group(2));
// look for each port object
while (matcherPortObject.find()) {
String poName = matcherPortObject.group(1);
int poQty = Integer.parseInt(matcherPortObject.group(2));
int poPrice = Integer.parseInt(matcherPortObject.group(3));
portObjectList.add(new PortObject(poName, poQty, poPrice));
}
portList1.add(new Port(name, portObjectList));
}
}
// Print
System.out.println("PortList1:\n" + portList1 + "\n");
}
The output is
PortList1:
[Port:PORT [AXN:0:10, BGT:20:30, CXZ:10:30], Port:BENCH [AXN:50:10, BGT:30:30, DFG:30:20, XYZ:0:10]]
Most likely a better idea: Split + Stream
Honestly speaking, I didn't know that split can directly be turned into a Stream. It basically leverages the Pattern.compile(yourRegex).splitAsStream(yourInput). As I said, this is a long split way but as the split are dispatched in Port constructor and PortObject constructor, it makes it easier to read:
Put in code, it is much shorter:
public static void main(String[] args) throws IOException {
// the input
String a = "PORT:AXN,0,10;BGT,20,30;CXZ,10,30|BENCH:AXN,50,10;BGT,30,30;DFG,30,20;XYZ,0,10";
// Option 2: Split with Stream:
List<Port> portList2 = Pattern.compile("\\|").splitAsStream(a)
.map(Port::new)
.collect(Collectors.toList());
// Print
System.out.println("PortList2:\n" + portList2 + "\n");
}
Obviously, the output is the same:
PortList2:
[Port:PORT [AXN:0:10, BGT:20:30, CXZ:10:30], Port:BENCH [AXN:50:10, BGT:30:30, DFG:30:20, XYZ:0:10]]

Trying to split up a string with blank space

I'm writing out a piece of a code that where I am trying to split up the user's input into 3 different arrays, by using the spaces in-between the values the user has entered. However, everytime i run the code i get the error:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
at Substring.main(Substring.java:18)
Java Result: 1
I have tried to use a different delimiter when entering the text and it has worked fine, e.g. using a / split the exact same input normally, and did what i wanted it to do thus far.
Any help would be appreciated!
Here's my code if needed
import java.util.Scanner;
public class Substring{
public static void main(String[]args){
Scanner user_input = new Scanner(System.in);
String fullname = ""; //declaring a variable so the user can enter their full name
String[] NameSplit = new String[2];
String FirstName;
String MiddleName;
String LastName;
System.out.println("Enter your full name (First Middle Last): ");
fullname = user_input.next(); //saving the user's name in the string fullname
NameSplit = fullname.split(" ");//We are splitting up the value of fullname every time there is a space between words
FirstName = NameSplit[0]; //Putting the values that are in the array into seperate string values, so they are easier to handle
MiddleName = NameSplit[1];
LastName = NameSplit[2];
System.out.println(fullname); //outputting the user's orginal input
System.out.println(LastName+ ", "+ FirstName +" "+ MiddleName);//outputting the last name first, then the first name, then the middle name
new StringBuilder(FirstName).reverse().toString();
System.out.println(FirstName);
}
}
Split is a regular expression, you can look for one or more spaces (" +") instead of just one space (" ").
String[] array = s.split(" +");
Or you can use Strint Tokenizer
String message = "MY name is ";
String delim = " \n\r\t,.;"; //insert here all delimitators
StringTokenizer st = new StringTokenizer(message,delim);
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
You have made mistakes at following places:
fullname = user_input.next();
It should be nextLine() instead of just next() since you want to read the complete line from the Scanner.
String[] NameSplit = new String[2];
There is no need for this step as you are doing NameSplit = user_input.split(...) later but it should be new String[3] instead of new String[2] since you are storing three entries i.e. First Name, Middle Name and the Last Name.
Here is the correct program:
class Substring {
public static void main (String[] args) throws java.lang.Exception {
Scanner user_input = new Scanner(System.in);
String[] NameSplit = new String[3];
String FirstName;
String MiddleName;
String LastName;
System.out.println("Enter your full name (First Middle Last): ");
String fullname = user_input.nextLine();
NameSplit = fullname.split(" ");
FirstName = NameSplit[0];
MiddleName = NameSplit[1];
LastName = NameSplit[2];
System.out.println(fullname);
System.out.println(LastName+ ", "+ FirstName +" "+ MiddleName);
new StringBuilder(FirstName).reverse().toString();
System.out.println(FirstName);
}
}
Output:
Enter your full name (First Middle Last): John Mayer Smith
Smith, John Mayer
John
java.util.Scanner breaks its input into tokens using a delimiter pattern, which by default matches whitespace.
hence even though you entered 'Elvis John Presley' only 'Elvis' is stored in the fullName variable.
You can use BufferedReader to read full line:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
fullname = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
or you can change the default behavior of scanner by using:
user_input.useDelimiter("\n"); method.
The exception clearly tells that you are exceeding the array's length. The index 2 in LastName = NameSplit[2] is out of array's bounds. To get rid of the error you must:
1- Change String[] NameSplit = new String[2] to String[] NameSplit = new String[3] because the array length should be 3.
Read more here: [ How do I declare and initialize an array in Java? ]
Up to here the error is gone but the solution is not correct yet since NameSplit[1] and NameSplit[2] are null, because user_input.next(); reads only the first word (*basically until a whitespace (or '\n' if only one word) is detected). So:
2- Change user_input.next(); to user_input.nextLine(); because the nextLine() reads the entire line (*basically until a '\n' is detected)
Read more here: [ http://www.cs.utexas.edu/users/ndale/Scanner.html ]

Splitting team names with scores separated by space and commas

I've multiple lines of game scores as input. The input is something like this.
Lions 1, FCAwesome 1
I'm currently Splitting the line based on either comma or space.
Charset charset = Charset.forName("US-ASCII");
String REGEX = ",?\\s+";
Pattern pattern = Pattern.compile(REGEX);
try(BufferedReader reader = Files.newBufferedReader(path, charset)){
int count = 0;
String line = null;
while((line = reader.readLine()) != null){
String[] arr = pattern.split(line);
}
This works fine for the provided input. However if the team name is has more than one word, my code breaks.
Lions 1, FC Awesome 1
How do I modify my REGEX to handle this case. FC Awesome still needs to be one team name.
Try splitting on space which
has comma before it (including that comma) - to separate team score pairs.
has digit after it - to separate team name and score,
So try with split(",\\s|\\s(?=\\d)")
If there is possible that some parts of team name can start with digit, we can specify our condition more. We can require from [space][digit] to either have after it comma or to be placed at the end of text.
split(",\\s|\\s(?=\\d+(?=,|$))")
try to split whole data by comma, then use getTeam method below
class Team {
String name;
int score;
public Team(String name, int score) {
this.name = name;
this.score = score;
}
#Override
public String toString() {
return this.name + ", " + this.score;
}
public static Team getTeam(String data) {
String score = "";
int i = data.length() - 1;
for (; Character.isDigit(data.charAt(i)); i--) {
score += data.charAt(i);
}
String name = data.substring(0, i);
return new Team(name, Integer.parseInt(new StringBuilder(score).reverse().toString()));
}
}
for example input is like this
LION_## 1234 OLD 5555 ,TEAM2345NAME NAME 123NAME 4444
first name is LION_## 1234 OLD and it's score is 5555
second name is TEAM2345NAME NAME 123NAME and 4444 is it's score
note: both contain numbers or special characters in their name and even space in score part.
now all i need is creating an instance of Team class.like below example:
String all_data = "LION_## 1234 OLD 5555 ,TEAM2345NAME NAME 123NAME 4444";
// spliting data by comma
String parts[] = all_data.split(",");
// calling getTeam method
Team t1 = Team.getTeam(parts[0]);
Team t2 = Team.getTeam(parts[1]);
then use it's fields, for example print them.
System.out.println(t1.name);
System.out.println(t2.score);

Reading file into HashMap

I am reading a file with the city and its population.
the file looks like this:
New York city
NY 8,175,133
Los Angeles city
CA 3,792,621
............
The format of the original file is different but I can't modify my code to read it properly. The original file looks like this:
New York city NY 8,175,133
Los Angeles city CA 3,792,621
...........
I posted the code (below) that works for the first version, but how can I make it to work for the original format? I am trying to make the city my key and the state & population as the value.
I know it's something simple but I can't figure out what it is.
public static void main(String[] args) throws FileNotFoundException
{
File file = new File("test.txt");
Scanner reader = new Scanner(file);
HashMap<String, String> data = new HashMap<String, String>();
while (reader.hasNext())
{
String city = reader.nextLine();
String state_pop = reader.nextLine();
data.put(city, state_pop);
}
Iterator<String> keySetIterator = data.keySet().iterator();
while (keySetIterator.hasNext())
{
String key = keySetIterator.next();
System.out.println(key + "" + data.get(key));
}
}
Thank you.
Just replace your code where you call readLine with something like this:
String line = scannner.readLine();
int space = line.lastIndexOf(' ', line.lastIndexOf(' ') - 1);
String city = line.substring(0,space);
String statepop = line.substring(space+1);
then put your city and statepop into your map.
Basically, this code finds the second last space and splits your String there.
Perhaps something like:
while (reader.hasNext())
{
String line = reader.nextLine();
int splitIndex = line.lastIndexOf(" city ");
data.put(line.substring(0, splitIndex + 5), line.substring(splitIndex + 6));
}

string tokenizer stopping after first line

I have a text file I am trying to break up with string tokenizer. Here is a few lines of the text file:
Mary Smith 1
James Johnson 2
Patricia Williams 3
I am trying to break up into first name, last name and Customer ID.
I have so far been able to do that but it stops after mary smith.
Here is my code:
public static void createCustomerList(BufferedReader infileCust,
CustomerList customerList) throws IOException
{
String firstName;
String lastName;
int custId;
//take first line of strings before breaking them up to first last and cust ID
String StringToBreak = infileCust.readLine();
//split up the string with string tokenizer
StringTokenizer st = new StringTokenizer(StringToBreak);
firstName = st.nextToken();
while(st.hasMoreElements())
{
lastName = st.nextToken();
custId = Integer.parseInt(st.nextToken());
CustomerElement CustomerObject = new CustomerElement();
CustomerObject.setCustInfo(firstName,lastName,custId);
customerList.addToList(CustomerObject);
}
}
String StringToBreak = infileCust.readLine();
reads the FIRST line from the file. And you feed the StringTokenizer with it. It's normal that StringTokenized doesn't find more tokens.
You have to create a second loop enclosing all this to read every line. It is:
outer loop: readLine until it gets null {
create a StringTokenizer that consumes *current* line
inner loop: nextToken until !hasMoreElements()
}
Well, indeed you don't need to do an inner loop because you have three different fields. It's enough with:
name = st.nextToken();
lastName = st.nextToken();
id = st.nextToken;
For the outer loop, you need to store the contents of the current line in the stringToBreak variable so that you can access it inside the loop.
You need a new StringTokenizer for each line so it needs to be inside the loop.
String stringToBreak = null;
while ((stringToBreak = infileCust.readLine()) != null) {
//split up the string with string tokenizer
StringTokenizer st = new StringTokenizer(stringToBreak);
firstName = st.nextToken();
lastName = st.nextToken();
custId = Integer.parseInt(st.nextToken());
}
First off, you want to look at your loop, specifically how you have firstName outside of the loop so that is going to throw all of you tokens off. You will be trying to create new customer objects without enough information.

Categories

Resources