I'm working on this assignment I'm supposed to read from a text file like this...
Student Name: John
Student ID: 12344/19
College: Science
Credits Attempted: 15
Credits Earned: 15
Grade Points: 41.2
Course Code Course Title Credit Grade
COMP1007, Amazing Applications of AI, 2, B
COMP2202, Fund. of Object Oriented Prog., 3, C-
MATH2108, Calculus (2), 3, C-
MATH3340, Discrete Math. for Comp. Sci., 3, B-
STAT2101, Introduction to Statistics, 4, C+
I should read this text file and calculate the GPA of the student and create an output file that should look like this...
Output text file
So basically I'm stuck and I have no idea what I to do...
I know how to read line by line and split a line into different parts, but this doesn't seem to work here since every line is different from the other. For example the first line has two parts, the "Student Name" and the name itself in this case "John". But in line 9, there are four different parts, the course code, course name, credit and grade.
I'm honestly not looking to cheat on the assignment but only to understand it
help :)
Note I can't use Stream or Hashmap or BufferedReader
Each data record in a text file always has a Start and an End. The easiest records are obviously those that are contained on a single delimited line within the text file, where each file line is in fact a record as you can see within a typical CSV format data file. The harder records to read are the Multi-Line records whereas each data record consists of several sequential text file lines but still, there is a Start and a End to each record.
The Start of a record is usually pretty easy to distinguish. For example, in the file example you provided in your post it is obviously any file line that starts with Student Name:.
The End of a record may not always be so easy to determine since many applications do not save fields which contain no data value in order to help increase access speed and reduce file bloat. The thought is "why have a text file full of empty fields" and to be honest, rightly so. I'm not a big fan of text file records anyways since utilizing a database would make far better sense for larger amounts of data. In any case, there will always be a file line that will indicate the Start of a record so it would make sense to read from Start to Start of the next record or in the case of the last record in file, from Start to End Of File (EOF).
Here is an example (read the comments in code):
// System line separator to use in files.
String ls = System.lineSeparator();
/* Array will hold student data: Student Name, Student ID, College,
Credits Attempted, Credits Earned, and finally Grade Points. */
String[] studentData = new String[6];
// String Array to hold Course Table Header Names.
String[] coursesHeader = {"COURSE NO", "COURSE TITLE", "CREDITS", "GRADE"};
// List Interface to hold all the course Data line Arrays for each record
java.util.List<String[]> cousesList = new java.util.ArrayList<>();
// Underlines to be used for Console display and file records
// Under courses Header
String underline1 = "-------------------------------------------------------------";
// Under all the courses
String underline2 = "------------------------------------------------------------------------------------";
/* Read and Write to files using 'Try With Resources' so to
automatically close the reader an writer objects. */
try (Scanner reader = new Scanner(new java.io.File("StudentData.txt"), "UTF-8");
java.io.Writer writer = new java.io.FileWriter("StudentsGPA.txt")) {
// For console display only! [Anything (except caught errors) to Console can be deleted]
System.out.println("The 'StudentsGPA.txt' file will contain:");
System.out.println("======================================");
System.out.println();
// Will hold each line read from the reader
String line = "";
/* Will hold the name for the next record. This would be the record START
but only AFTER the first record has been read. */
String newName = "";
// Start reading the 'StudentData.txt' file (line by line)...
while (reader.hasNextLine()) {
/* If newName is empty then we're on our first record or
there is only one record in file. */
if (newName.isEmpty()) {
line = reader.nextLine(); // read in a file line...
}
else {
/* newName contains a name so we must have bumped into
the START of a new record during processing of the
previous record. We aleady now have the first line
of this new record (which is the student's name line)
currently held in the 'newName' variable so we just
make 'line' equal what is in the 'newName' variable
and carry on processing the data as normal. in essance,
we simply skipped a read because we've already read it
earlier when processing the previous record. */
line = newName;
// Clear this variable in preparation for another record START.
newName = "";
}
/* Skip comment lines (lines that start with a semicolon (;)
or a hash mark (#). Also skip any blank lines. */
if (line.startsWith(";") || line.startsWith("#") || line.isEmpty()) {
continue;
}
/* Does this file line start with 'Student Name:'? If so then
this is a record START, let's process this record. If not
then just keep reading the file. */
if (line.startsWith("Student Name:")) {
/* Let's put the student name into the studentData array at
index 0. If it is detected that there has been no name
applied for some reason then we place "N/A" as the name.
We use a Ternary Operator for this. So, "N/A" will be a
Default if there is not name. This will be typical for
the other portions of student data. */
studentData[0] = line.isEmpty() ? "N/A" : line.split("\\s*:\\s*")[1].trim();
/* Let's keep reading the file from this point on and retrieve
the other bits of student data to fill the studentData[]
Array... */
for (int i = 1; i < 6; i++) {
line = reader.nextLine().trim();
/* If we encounter a comment line or a blank line then let's
just skip past it. We don't want these. */
if (line.startsWith(";") || line.startsWith("#") || line.isEmpty()) {
i--;
continue;
}
/* Store the other portions of student data into the
studentData Array using "N/A" as a default should
any student data field contain nothing. */
studentData[i] = line.isEmpty() ? "N/A" : line.split("\\s*:\\s*")[1].trim();
}
// The current Student's Courses...
/* Clear the List Interface object in preparation for new
Courses from this particular record. */
cousesList.clear();
// Read past the courses header line...We don't want it.
reader.nextLine();
// Get the courses data (line by line)...
while (reader.hasNextLine()) {
line = reader.nextLine().trim();
/* Again, if we encounter a comment line or a blank line
in this section then let's just skip past it. We don't
want these. */
if (line.startsWith(";") || line.startsWith("#") || line.isEmpty()) {
continue;
}
/* At this point, if we have read in a line that starts
with 'Student Name:' then we just hit the START of a
NEW Record! This then means that the record we're
currently working on is now finished. Let's store this
file line into the 'newRecord' variable and then break
out of this current record read. */
if (line.startsWith("Student Name:")) {
newName = line;
break;
}
/* Well, we haven't reached the START of a New Record yet
so let's keep creating the courses list (line by line).
Break the read in course line into a String[] array.
We use the String#split() method for this with a small
Regular Expression (regex) to split each line based on
comma delimiters no matter how the delimiter spacing
might be (ex: "," " ," " , " or even " , "). */
String[] coursesData = line.split("\\s*,\\s*");
/* Add this above newly created coursesData string array
to the list. */
cousesList.add(coursesData);
}
/* Write (append) this current record to new file. The String#format()
method is used here to save the desired data into the 'StudentGPA.txt'
file in a table style format. */
// Student Data...
writer.append(String.format("%-12s: %-25s", "ID", studentData[1])).append(ls);
writer.append(String.format("%-12s: %-25s", "Name", studentData[0])).append(ls);
writer.append(String.format("%-12s: %-25s", "College", studentData[2])).append(ls);
// Student Courses...
// The Header line
writer.append(String.format("%-13s %-30s %-10s %-4s", coursesHeader[0],
coursesHeader[1], coursesHeader[2], coursesHeader[3])).append(ls);
// Apply an Underline (underline1) under the header.
writer.append(underline1).append(ls);
// Write the Courses data in a table style format to make the Header format.
for (String[] cData : cousesList) {
writer.append(String.format("%-13s %-33s %-9s %-4s",
cData[0], cData[1], cData[2], cData[3])).append(ls);
}
// Apply an Underline (underline2) under the Courses table.
writer.append(underline2).append(ls);
// Display In Console Window (you can delete this if you want)...
System.out.println(String.format("%-12s: %-25s", "ID", studentData[1]));
System.out.println(String.format("%-12s: %-25s", "Name", studentData[0]));
System.out.println(String.format("%-12s: %-25s", "College", studentData[2]));
System.out.println(String.format("%-13s %-30s %-10s %-4s", coursesHeader[0],
coursesHeader[1], coursesHeader[2], coursesHeader[3]));
System.out.println(underline1);
for (String[] cData : cousesList) {
System.out.println(String.format("%-13s %-33s %-9s %-4s",
cData[0], cData[1], cData[2], cData[3]));
}
System.out.println(underline2);
// The LAST line of each record, the Credits...
// YOU DO THE CALCULATIONS FOR: totalAttemped, semGPA, and cumGPA
String creditsAttempted = studentData[3];
String creditsEarned = studentData[4];
int credAttempted = 0;
int credEarned = 0;
int totalAttempted = 0;
double semGPA = 0.0d;
double cumGPA = 0.0d;
/* Make sure the 'credits attemted' numerical value is in fact
a string representaion of an integer value. if it is then
convert that string numerical value to integer. */
if (creditsAttempted.matches("\\d+")) {
credAttempted = Integer.valueOf(creditsAttempted);
}
/* Make sure the 'credits earned' numerical value is in fact
a string representaion of an integer value. if it is then
convert that string numerical value to integer. */
if (creditsEarned.matches("\\d+")) {
credEarned = Integer.valueOf(creditsEarned);
}
// Build the last record line (the Credits string) with the acquired data.
String creditsString = new StringBuilder("CREDITS: TOTAL.ATTEMPTED ")
.append(totalAttempted).append("? EARNED ").append(credEarned)
.append(" ATTEMPTED ").append(credAttempted).append(" SEM GPA ")
.append(semGPA).append("? CUM GPA ").append(cumGPA).append("?")
.toString();
// Display it to the console Window (you can delete this).
System.out.println(creditsString);
System.out.println();
// Write the built 'credit string' to file which finishes this record.
writer.append(creditsString).append(ls);
writer.append(ls); // Blank Line in preparation for next record.
writer.flush(); // Flush the data buffer - write record to disk NOW.
}
}
}
// Trap Errors...Do whatever you want with these.
catch (FileNotFoundException ex) {
System.err.println("File Not Found!\n" + ex.getMessage());
}
catch (IOException ex) {
System.err.println("IO Error Encountered!\n" + ex.getMessage());
}
Yes, it looks long but if you get rid of all the comments you can see that it really isn't. Don't be afraid to experiment with the code. Make it do what you want.
EDIT: (as per comments)
To place the student info portion of each record into an ArrayList so that you can parse it the way you want:
Where the forloop is located within the example code above for gathering the student info, just change this loop to this code and parse the data the way you want:
// Place this ArrayList declaration underneath the 'underline2' variable declaration:
java.util.ArrayList<String> studentInfo = new java.util.ArrayList<>();
then:
if (line.startsWith("Student Name:")) {
studentInfo.clear();
studentInfo.add(line);
/* Let's keep reading the file from this point on and retrieve
the other bits of student data to fill the studentData[]
Array... */
for (int i = 1; i < 6; i++) {
line = reader.nextLine().trim();
/* If we encounter a comment line or a blank line then let's
just skip past it. We don't want these. */
if (line.startsWith(";") || line.startsWith("#") || line.isEmpty()) {
i--;
continue;
}
studentInfo.add(line);
}
// .................................................
// .... The rest of the code for this `if` block ...
// .................................................
}
You will of course need to change the code after this loop to properly represent this ArrayList.
OK, so here's how you do it ...
You read in all of the file and store each line in a List<String>
For the first 8 lines you process each one in a separate way. You can even write a separate function to parse the necessary info out of every line for lines 0-7
All the remaining lines have identical structure. Therefore, you can process them all in the same way to parse out and then process the necessary data.
And a comment to this answer if something is unclear and I'll clarify.
I have an Excel file that I converted to a CSV and need to create an ArrayList of each column of data. I don't want to manually add each data point. Is there a way to use an iterator to only read in one column at a time and then save that as an ArrayList?
I made a method that can read in the whole CSV and save it as an ArrayList using a Scanner.
The CSV file is called "Mineral Database NA.csv". An example of a column of data I am trying to isolate has the title "mineralNames", no mineral name is repeated, each name is a string, the delimiter is a comma.
private static final File file = new File("../input/Mineral Database NA Final.csv");
private ArrayList<String> dataList;
public IterList()
{
dataList = new ArrayList<String>();
}
/** Reads file and adds info to dataList row by row.
*/
public void readFile() throws IOException {
Scanner fileReader = new Scanner(file);
while (fileReader.hasNext()) {
String line = fileReader.nextLine();
dataList.add(line);
}
}
I expect an ArrayList of strings that belong to a particular column.
You can get the contents of a specific column by splitting the String you receive from your nextLine() operation.
Once your line field is split into tokens, you can use the index of the specific column you're after and add only that token to the ArrayList.
So, picking up from where you set line:
String[] tokens = line.split(",");
// Now, token 0 corresponds with the first column from the left
// token 1 corresponds with the second column from the left, etc.
// Let's assume then, you want the third column
dataList.add(tokens[2];
Adding that code to your while loop should get you what you want after you make your dataList object available to your readFile method.
The one caveat here is that you need to be sure that your CSV file is regular, with each column represented on each line, or else you're going to have to come up with code that deals with out of bound indices.
I have to create a java program which opens a file containing integers. An input dialog box asks the use the name of the input file, and then an output dialog box shows the values read from the data file, each on a different line, and then the number of values from the file. I am confused
as to what is missing from my code. The input dialog box appears but I am completely stuck after that.
import java.io.*;
import javax.swing.*;
import java.util.Scanner;
public class prob2
{
public static void main (String [] args) throws IOException
{
String name, out="", out2="" ;
int quantity=0, val;
name=JOptionPane.showInputDialog("Enter the name of the file to be opened: ");
Scanner inputFile= new Scanner(new FileReader(name));
while(inputFile.hasNextInt());
{
val=inputFile.nextInt();
out= val+"\n";
quantity++;
}
JOptionPane.showMessageDialog(null,
val + quantity);
inputFile.close();
}
}
Your initial problem is right here
while(inputFile.hasNextInt()); // <- Or here actually...
This is basically setting up an infinite loop instead of executing the block of ode you want
Start by removing the ; at the end of the line...
while(inputFile.hasNextInt())
{
val=inputFile.nextInt();
out= val+"\n";
quantity++;
}
Once you've that, you will to change
JOptionPane.showMessageDialog(null,
val + quantity);
to
JOptionPane.showMessageDialog(null,
out + quantity);
to overcome the compiler error
And finally, you will need to change...
out = val + "\n";
in your while-loop to
out += val + "\n";
so that you are actually concatenating the result
(and yes, we should be using StringBuilder, but I think we can let that one slide for now)
You have to collect the input you are reading in and that you want to later print.
For example by creating a StringBuilder instance and appending the content you wish to present to the user.
When you are done reading you pass builder.toString() to another option pain as message.
I have a text file that contains something like:
parameter 1 = true
parameter 2 = true
parameter 3 = false
and this goes on for over 90 parameters. I am creating a program that will read from this file and create buttons that will have different states depending if the parameter is set to true or false, and also alternate between these states.
My question is: To make this buttons, is there a way to create a constructor to be called that can create the buttons for me or do i have to copy and paste everything, one at a time? As I am relatively new to programming, I've been looking for days but I was never able to make it.
PS: I don't need the code for the states of the buttons, I just need to know if there is a simpler and quice, and if there is, how do I do it.
Assuming you don't need the names from the text file (parameter 1, etc), one possible solution is to try going through the text file and save each value to a boolean array. Then you can do something like this:
List<MyButton> buttons = new ArrayList<MyButton>();
for (int i = 0; i < buttonValues.length; i++) { //buttonValues is the array of booleans
buttons.add(new MyButton(buttonValues[i]);
}
Where MyButton is a class you make that has a constructor that takes a boolean value to indicate its state.
You don't even need to save the values in an array, you could skip straight to making the buttons as you read the file. You would change it to a while loop with the condition along the lines of myFileReader.hasNext() and pass the constructor the boolean value as you read it. You could also easily read the name of the parameter here as well, if you want to keep track of it. Just update MyButton's constructor to take the name.
You can read line by line and create a buttons in runtime for each line :
public Frame() throws FileNotFoundException, IOException {
try (BufferedReader br = new BufferedReader(new FileReader("1.txt"))) {
String line;
while ((line = br.readLine()) != null) {
// read line by line
String[] paramAndValue = new String[2];
// split key/value
paramAndValue = line.split(" = ");
// add a Jbutton with the key as name
JButton button = new JButton(paramAndValue[0]);
// and the value as the state
button.setEnabled(Boolean.parseBoolean(paramAndValue[1]));
this.add(button);
}
}
}
I need to read a text file and store the text in the file in five different arrays. The text file contains questions, four options and the correct answer in one line. I used the scanner to read the textfile and store the whole text as a string and then was trying to use the string tokenizer to differentiate the questions and the options so i could store them in their respective errors. The compiler gives me error when i try doing this:
public void readFile()
{
while (reader.hasNext())
{
String allText = reader.next();
StringTokenizer tokenizer = new StringTokenizer(allText, ",");
while (tokenizer.hasMoreElements())
{
question[index] = tokenizer.nextElement();
}
}
}
If question is a String, you can't add elements to it. You need to create a collection of Strings in order to add a String to it (you can use Array, List, Set etc.)