I'm pretty new to Java (currently enrolled in my first programming class), so the answer to my question could be right in front of me.
My goal is to read in a text file that reads as follows:
4563123,112211324135412
2312311,222121324135211
2312345,112211324135421
5527687,212111313124412
7867567,111111111111111
where the first set of numbers is an ID, and the second set is a set of answers to a test (of which I have the key). Once I've read in the answers I need to store them in an array (and I assume just an array, as my class has not covered ArrayLists yet). All of this data would need to be stored in one array, since I need to return it at the end of the method.
Here is the code that I have so far:
public static String[] readFile(String filename)throws IOException{
Scanner inFile = new Scanner(filename);
String line;
String[] results = new String[101];
int i = 0;
while (inFile.hasNextLine()){
line = inFile.nextLine();
String[] incoming = line.split(",");
String wid = incoming[0];
String answer = incoming[1];
results[i] = wid;
results[i + 1] = answer;
i += 2;
}
inFile.close();
return results;
}
It's safe to ignore the String filename, it was passed in from the main.
Every time I run this method, I keep running into an ArrayOutOfBoundsException, mainly when I try to assign incoming[1] to answer.
I've been staring at this code longer than what is probably good for me, but it seems that I just can't wrap my head around it. Any help would be appreciated, whether that be telling me what is wrong or what I can do to improve.
Since you are using Java 7, use the new Files API and a try-with-resources statement; also, avoid copying empty lines:
final Path file = Paths.get(filename);
String line;
String[] incoming;
String[] results = new String[101];
int nrElements = 0;
try (
final BufferedReader reader = Files.newBufferedReader(path,
StandardCharsets.UTF_8);
) {
while ((line = reader.readLine()) != null) {
incoming = line.split(",");
if (incoming.length != 2)
continue;
results[nrElements++] = incoming[0];
results[nrElements++] = incoming[1];
}
}
return Arrays.copyOfRange(results, 0, nrElements);
Either you are having an empty line in your file. Or a garbage line that doesn't have any comma in it. So before accessing the slitted array, just give a check.
if(incoming != null && incoming.length >= 2){
String wid = incoming[0];
String answer = incoming[1];
// ... other dependent codes go here!
}
It will help you to avoid the Exception.
Related
I'm very new (6 weeks into java) trying to remove elements from a csv file that lists a set of students as such (id, name, grades) each on a new line.
Each student id is numbered in ascending value. I want to try and remove a student by entering the id number and I'm not sure how I can do this.
So far I've just tried to reduce the value that user inputs to match the index as students are listed by number and I did this in a while loop. However, each iteration doesn't recognize the reduction from the previous user Input, and I think I need a way that can just search the value of the id, and remove the entire line from the csv file.
Have only tried to include the pertinent code. Reading previous stack questions has shown me a bunch of answers related to nodes, which make no sense to me since I don't have whatever prerequisite knowledge is required to understand it, and I'm not sure the rest of my code is valid for those methods.
Any ideas that are relatively simple?
Student.txt (each on a new line)
1,Frank,West,98,95,87,78,77,80
2,Dianne,Greene,78,94,88,87,95,92
3,Doug,Lei,78,94,88,87,95,92
etc....
Code:
public static boolean readFile(String filename) {
File file = new File("C:\\Users\\me\\eclipse-workspace\\studentdata.txt");
try {
Scanner scanner = new Scanner(file);
while(scanner.hasNextLine()) {
String[] words=scanner.nextLine().split(",");
int id = Integer.parseInt(words[0]);
String firstName = words[1];
String lastName = words[2];
int mathMark1 = Integer.parseInt(words[3]);
int mathMark2 = Integer.parseInt(words[4]);
int mathMark3 = Integer.parseInt(words[5]);
int englishMark1 = Integer.parseInt(words[6]);
int englishMark2 = Integer.parseInt(words[7]);
int englishMark3 = Integer.parseInt(words[8]);
addStudent(id,firstName,lastName,mathMark1,mathMark2,mathMark3,englishMark1,englishMark2,englishMark3);
}scanner.close();
}catch (FileNotFoundException e) {
System.out.println("Failed to readfile.");
private static void removeStudent() {
String answer = "Yes";
while(answer.equals("Yes") || answer.equals("yes")) {
System.out.println("Do you wish to delete a student?");
answer = scanner.next();
if (answer.equals("Yes") || answer.equals("yes")) {
System.out.println("Please enter the ID of the student to be removed.");
//tried various things here: taking userInput and passing through linkedlist.remove() but has never worked.
This solution may not be optimal or pretty, but it works. It reads in an input file line by line, writing each line out to a temporary output file. Whenever it encounters a line that matches what you are looking for, it skips writing that one out. It then renames the output file. I have omitted error handling, closing of readers/writers, etc. from the example. I also assume there is no leading or trailing whitespace in the line you are looking for. Change the code around trim() as needed so you can find a match.
File inputFile = new File("myFile.txt");
File tempFile = new File("myTempFile.txt");
BufferedReader reader = new BufferedReader(new FileReader(inputFile));
BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile));
String lineToRemove = "bbb";
String currentLine;
while((currentLine = reader.readLine()) != null) {
// trim newline when comparing with lineToRemove
String trimmedLine = currentLine.trim();
if(trimmedLine.equals(lineToRemove)) continue;
writer.write(currentLine + System.getProperty("line.separator"));
}
writer.close();
reader.close();
boolean successful = tempFile.renameTo(inputFile);
So I want to create a constructor that reads in a line of a file from a csv and save the first token into a variable and the remaining tokens into an array. This constructor will be used in a gradebook application but being new to txt/file manipulation I'm having a hard time.
A line will look like:
Billy Bob,68,79,95,83
I want to separate the tokens into these:
name = Billy Bob
grades[] = "68,79,95,83"
here is the code I have so far:
import java.io.*;
public class gradeBook {
public static void main(String[] args){
System.out.println("Java Grade Book version 1.0");
int lineCounter = 0;
String array[];
try{
File data = new File("/file/path/that/works");
InputStream f = new FileInputStream(data);
BufferedReader br = new BufferedReader(new InputStreamReader(f));
for (String line = br.readLine(); line != null; line = br.readLine()) {
System.out.println(line); // just here to check that the code is working thus far
//insert code here
//name should equal first token (which is two names like Billy Bob)
//grades[] should contain the other double type tokens (e.g. 56,87,89,90)
}
br.close();
}
catch(Exception e){
System.err.println("Error: File Couldn't Be Read");
}
}
}
And I want to loop through the file to get as many students as are on the file stored so I can manipulate the grades for averages among other things. This is a personal project to help improve my developing skills so any help, useful tutorial links, and tips will be greatly appreciated. But please don't suggest simplistic examples like the many tutorials I have already read that only use one data type.
Thanks for any help!
Split the line into an array;
String[] input = line.split(",");
String variable = input[0];
int[] grades= new int[input.lenght - 2];
for(int i = 1; i < input.length; i++)
{
grades[i] = input[i];// you might have to do Integer.pareseInt(input[i]);
}
I did not write this in an IDE, but the logic should be correct.
You are going to run into a new problem. You grade book will only contain the last entry. Try using a 2D array for grades and 1D array for names; I personally would not use arrays. I would use arraylist.
So I haven't tested computing my tokens with methods or anything else yet but I have tokenized the line to sum (ha ha oops, meant some) degree with this bit of code:
String[] tokens = line.split(",");
String name = tokens[0];
String grade1 = tokens[1];
String grade2 = tokens[2];
String grade3 = tokens[3];
String grade4 = tokens[4];
I have created a method that reads specific lines from a file based on their line number. It works fine for most files but when I try to read a file that contains a large number of really long lines then it takes ages, particularly as it gets further down in the file. I've also done some debugging and it appears to take a lot of memory as well but I'm not sure if this is something that can be improved. I know there are some other questions which focus on how to read certain lines from a file but this question is focussed primarily on the performance aspect.
public static final synchronized List<String> readLines(final File file, final Integer start, final Integer end) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
List<String> lines = new ArrayList<>();
try {
String line = bufferedReader.readLine();
Integer currentLine = 1;
while (line != null) {
if ((currentLine >= start) && (currentLine <= end)) {
lines.add(line + "\n");
}
currentLine++;
if (currentLine > end) {
return lines;
}
line = bufferedReader.readLine();
}
} finally {
bufferedReader.close();
}
return lines;
}
How can I optimize this method to be faster than light?
I realised that what I was doing before was inherently slow and used up too much memory.
By adding all lines to memory and then processing all lines in a List it was not only taking twice as long but was also creating String variables for no reason.
I am now using Java 8 Stream and processing at point of reading which is the fastest method I've used so far.
Path path = Paths.get(file.getAbsolutePath());
Stream<String> stream = Files.lines(path, StandardCharsets.UTF_8);
for (String line : (Iterable<String>) stream::iterator) {
//do stuff
}
}
This problem takes a bit of explaining, I'll try to be as concise as possible:
I have am trying to initalise an array of Can objects, these objects only have 2 fields (both Strings): name, manufacturer
I am trying to initialise the fields by reading from a CSV file with the following format:
Tomatoes,Heinz
Legumes,Jerry
(no space between the lines, it's being formatted like that on this site for some reason)
The first string in each row is the value I want to be the name, the 2nd is the manufacturer.
So I've created a method to read each line of the CSV, which passes each line to a tokenizer method to extract single values:
private void readFile (String inFilename) {
FileInputStream fileStrm = null;
InputStreamReader rdr;
BufferedReader bufRdr;
int lineNum;
String line;
try {
fileStrm = new FileInputStream(inFilename);
rdr = new InputStreamReader(fileStrm);
bufRdr = new BufferedReader(rdr);
lineNum = 0;
line = bufRdr.readLine();
while {line != null) {
lineNum++;
processLine(line); //passes line to tokenizer
line = bufRdr.readLine();
}
fileStrm.close();
}
catch (IOException e) {
if (fileStrm != null) {
try { fileStrm.close(); } catch (IOException ex2) { }
}
System.out.println("Error in file processing: " + e.getMessage());
}
}
The lines are passed to this tokenizer method:
private String processLine(String csvRow) {
String thisToken = null;
StringTokenizer strTok;
strTok = new StringTokenizer(csvRow, ",");
while (strTok.hasMoreTokens()) {
thisToken = strTok.nextToken();
}
}
And that's where I get a bit stuck. To initialise my array I think I'd need a for loop, something like
for (int i=0; i<=array.length;i++)
{
array[i].name = readFile("filename.csv");
array[i].manufacturer = readFile("filename.csv");
}
But obviously this will not work. Can anyone suggest how I can go about this? I'd prefer to keep the code mostly intact and figure out a solution using the existing code.
Thanks
First thing: -
You are calling processLine(line);, but are not returning the token read from this method.. So, the token obtained in this method in engulped there only.. So, you should return something from that method..
Second:-
array[i].name = readFile("filename.csv");
array[i].manufacturer = readFile("filename.csv");
In the above code, you are calling readFile() each time for the two attributes.. So, even if you return somthing, these two attributes will be initialized to same value.. Because each time you are starting reading file from scratch..
Third thing: -
In fact your above code will not compile.. Because you are assigning the value of readFile() (which is actually not returning anything) to array.. So give a return type to this method.. It would be String.. And returning the tokens read..
EDIT: -
* I would suggest, you can use split() method of String class.. Tokenizer is not needed here, for justsplittingaround a singlecomma(,)`
Also, rather than using an array, you can use ArrayList, in which you can add your newly created object on the fly.. That way, you will not have to fix the size of array.. (And this is what you will want, as you don't know how much line you will have in your file right?)
Here's what you can do: -
Call the method readFile from somewhere, probably main()
readFile("filename.csv")
In your readFile() method, you can iterate over file to create an ArrayList like this: -
List<Can> yourList = new ArrayList<>();
while ((line = reader.readLine()) != null) {
String[] wordRead = line.split(',');
yourList.add(new Can(wordRead[0], wordRead[1]));
}
I assume, Can is the name of your class as you stated in your problem..
I have a multidimensional array built from Strings that is initially created with the size [50][50], this is too big and now the array is full of null values, I am currently trying to remove these said null values, I have managed to resize the array to [requiredSize][50] but cannot shrink it any further, could anyone help me with this? I have scoured the internet for such an answer but cannot find it.
Here is my complete code too (I realise there may be some very unclean parts in my code, I am yet to clean anything up)
import java.io.*;
import java.util.*;
public class FooBar
{
public static String[][] loadCSV()
{
FileInputStream inStream;
InputStreamReader inFile;
BufferedReader br;
String line;
int lineNum, tokNum, ii, jj;
String [][] CSV, TempArray, TempArray2;
lineNum = tokNum = ii = jj = 0;
TempArray = new String[50][50];
try
{
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Please enter the file path of the CSV");
String fileName = in.readLine();
inStream = new FileInputStream(fileName);
inFile = new InputStreamReader(inStream);
br = new BufferedReader(inFile);
StringTokenizer tok,tok2;
lineNum = 0;
line = br.readLine();
tokNum = 0;
tok = new StringTokenizer(line, ",");
while( tok.hasMoreTokens())
{
TempArray[tokNum][0] = tok.nextToken();
tokNum++;
}
tokNum = 0;
lineNum++;
while( line != null)
{
line = br.readLine();
if (line != null)
{
tokNum = 0;
tok2 = new StringTokenizer(line, ",");
while(tok2.hasMoreTokens())
{
TempArray[tokNum][lineNum] = tok2.nextToken();
tokNum++;
}
}
lineNum++;
}
}
catch(IOException e)
{
System.out.println("Error file may not be accessible, check the path and try again");
}
CSV = new String[tokNum][50];
for (ii=0; ii<tokNum-1 ;ii++)
{
System.arraycopy(TempArray[ii],0,CSV[ii],0,TempArray[ii].length);
}
return CSV;
}
public static void main (String args[])
{
String [][] CSV;
CSV = loadCSV();
System.out.println(Arrays.deepToString(CSV));
}
}
The CSV file looks as follows
Height,Weight,Age,TER,Salary
163.9,46.8,37,72.6,53010.68
191.3,91.4,32,92.2,66068.51
166.5,51.1,27,77.6,42724.34
156.3,55.7,21,81.1,50531.91
It can take any size obviously but this is just a sample file.
I just need to resize the array so that it will not contain any null values.
I also understand a list would be a better option here but it is not possible due to outside constraints. It can only be an multi dimensional array.
I think you need 3 changes to your program
After your while loop lineNum will be 1 more than the number of lines in the file so instead of declaring CSV to String[tokNum][50] declare it as CSV = new String[tokNum][lineNum-1];
tokNum will be the number of fields in a row so your for loop condition should be ii<tokNum rather than ii<tokNum-1
The last parameter for your arraycopy should be lineNum-1
i.e. the modified code to build your CSV array is:
CSV = new String[tokNum][lineNum-1];
for (ii=0; ii<tokNum ;ii++)
{
System.arraycopy(TempArray[ii],0,CSV[ii],0,lineNum-1);
}
and the output will then be:
[[Height, 163.9, 191.3, 166.5, 156.3], [Weight, 46.8, 91.4, 51.1, 55.7],
[Age, 37, 32, 27, 21], [TER, 72.6, 92.2, 77.6, 81.1],
[Salary, 53010.68, 66068.51, 42724.34, 50531.91]]
Notice that you don't really need to handle the first line of the file separately from the others but that is something you can cover as part of your cleanup.
10 to 1 this is a homework assignment. However, it looks like you've put somethought into it.
Don't make the TempArray variable. Make a "List of List of Strings". Something like:
List<List<String>> rows = new ArrayList<ArrayList<String>>();
while(file.hasMoreRows()) { //not valid syntax...but you get the jist
String rowIText = file.nextRow(); //not valid syntax...but you get the jist
List<String> rowI = new ArrayList<String>();
//parse rowIText to build rowI --> this is your homework
rows.add(rowI);
}
//now build String[][] using fully constructed rows variable
Here's an observation and a suggestion.
Observation: Working with (multidimensional) arrays is difficult in Java.
Suggestion: Don't use arrays to represent complex data types in Java.
Create classes for your data. Create a List of people:
class Person {
String height; //should eventually be changed to a double probably
String weight; // "
//...
public Person( String height, String weight /*, ... */ ) {
this.height = height;
this.weight = weight;
//...
}
}
List<Person> people = new ArrayList<Person>();
String line;
while ( (line = reader.nextLine()) != null ) {
String[] records = line.split(",");
people.add(new Person (records[0], records[1] /*, ... */));
}