I'm creating a utility class that will make it easier for people to parse a csv string and return an array of array of strings.
My code almost works, but for some reason, when I do a get on my result in my first row, I'm expecting to see 1 row and seeing several rows concatenated on the first row.
Quick example:
a,b,c,d
e,f,g,h
Expecting: {a,b,c,d}, {e,f,g,h}
Result: {a,b,c,d,e,f,g}
public class csvParse
{
protected String originalCSV;
protected int skipToLine;
protected ArrayList<ArrayList<String>> parsedList;
public ArrayList<ArrayList<String>> getParsedList()
{
return parsedList;
}
public void setParsedList(ArrayList<ArrayList<String>> parsedList)
{
this.parsedList = parsedList;
}
public csvParse(String incomingCSV, int skipToLine)
{
super();
this.originalCSV = incomingCSV;
this.skipToLine = skipToLine;
this.parsedList = new ArrayList<ArrayList<String>>();
execute();
}
protected void execute()
{
// breaking this so there's an error. read below
//TODO: Make sure you have data out to X. May use a try/catch?
String row;
String lines[] = this.originalCSV.split("\\n?\\r");
ArrayList<String> temp = new ArrayList<String>();
try{
for (int i = this.skipToLine; i < lines.length; i++)
{
row = lines[i];
//split on commas
String[] RowData = row.split(",");
for (int x = 0; x < RowData.length; x++)
{
temp.add(RowData[x]);
}
this.parsedList.add(temp);
}
}
finally{
}
}
}
In your execute() method you don't reset the temp variable, so it gets the data from all rows. Just move your initialization within the outer for-loop.
You create "temp" outside the loop that goes through the lines. Thus, you are continually adding fields to the same temp object. You want to move the creation of this inside the loop, so you create a new object for each line.
Also, note that you are not handling embedded commas within a field. Maybe this doesn't occur in your data. But the CSV standard is that a field may be enclosed in quotes, in which case the quotes should be stripped off. If it is enclosed in quotes, it can then contain commas. If a field includes quotes, they should be doubled. For example:
a,"b,c","He said, ""Hello"""
contains three fields:
a
b,c
He said, "Hello"
Related
working on a movie project but ran into some trouble.
If the file have many lines that look something like this:
Title: Avatar
Director: James Cameron
How do I make the method work?
The code is not running right now.
I created a helper method to find startswith what string first, so if the user puts in Title: or Director:, the method would return the name of either the tile or director. But java is saying that the i is not working here, it is strange, it said i can not be resolved to a variable. Also I realized if I do this, it would not work since the user is inputting a string but the line would contain both the info for title: name. How should I go about to make it work?
/**
* Lines in the file.
*/
private List<String> lines;
//helper method
String getNextStringStartsWith(String str) {
String[] lineArray;
//iterate over list of lines
for(i = 0; i < this.lines.size(); i++) {
//if the specific line has that string
if(this.lines.get(i).equals(str)) {
lineArray = this.lines.get(i).split(":");
info = lineArray[1].trim());
//gets the line afterwards
return info;
}
}
}
//Have to iterate over each line and look for lines starting with "Title:" and "Director:"
// Set the value of this.title and this.director
private void setTitleAndDirector() {
this.title = getNextStringStartsWith("Title:");
this.director = getNextStringStartsWith("Director:");
}
The problem with the for loop is that you didn't tell Java which type your variable i is. Therefore Java couldn't create a variable for it.
There were also some other small things wrong with your code.
You were missing a return value when nothing was returned from your loop.
You didn't tell Java which type the variable info is.
You had a ) too much on the line on the line with lineArray[1].trim().
Here is a way you can fix these things.
//helper method
String getNextStringStartsWith(String str) {
String[] lineArray;
//iterate over list of lines
for(int i = 0; i < this.lines.size(); i++) {
//if the specific line has that string
if(this.lines.get(i).equals(str)) {
lineArray = this.lines.get(i).split(":");
String info = lineArray[1].trim();
//gets the line afterwards
return info;
}
}
return null;
}
I have an assingment for school and I am having trouble with some ArrayLists. I have an input file which has one entry at every line. This entry has an integer and up to four strings. This input file is about locations that a film is filmed. The integer is the movieID in my case and the strings are the locations. However not every film has 4 locations which means that when my program tries to load the file it returns an error because it expects 5 fields at every row and this never happens because I have movies with 1 or 2 or the locations. I use a data loader class because I have to load several different files. My other files have a specific number of entries and fields at each row so loading those isn't a problem. The load process is done by adding the file into an array list and then creating the objects needed. I know that I need the program somehow to understand the empty fields and maybe handle them dynamically, for example a movie has 3 locations so the 4th field is empty, but I haven't figured it out yet. Any suggestions? Thank you!
This is my LocationsLoader class.
package dataLoader;
import java.util.ArrayList;
import dataModel.Locations;
public class LocationsLoader extends AbstractFileLoader<Locations>{
public int constructObjectFromRow(String[] tokens, ArrayList<Locations> locations) {
int movieID;
List<String> loc = new List();
movieID = Integer.parseInt(tokens[0]);
loc = tokens[]; // What goes here?
Locations l;
l = new Locations(movieID, loc);
locations.add(l);
System.out.println(l);
//System.out.println(locations.toString());
return 0;
}
}
And this is my Locations class:
package dataModel;
public class Locations {
private int movieID;
private List<String> loc;
public Locations(int otherMovieID, List<String> otherLocations) {
this.movieID = otherMovieID;
this.loc = otherLocations;
}
public int getMovieID() {
return movieID;
}
public void setMovieID(int id) {
this.movieID = id;
}
public String getLocations(int index) {
return loc.get(index);
}
}
}
You fill an array here
String[] tokens = new String[numFields];
for (int i = 0; i < numFields; i++) {
tokens[i] = tokenizer.nextToken();
}
but arrays are fixed length, there's really no reason to use them if you can have fewer values. Fill a list instead.
List<String> tokens = new ArrayList<>();
while (tokenizer.hasNextToken()) {
String token = tokenizer.nextToken().trim();
if (!token.isEmpty()) {
tokens.add(tokenizer.nextToken());
}
}
In fact, I'm not sure why you would need to give the reader the number of expected tokens at all.
But as Dodgy pointed out, you might as well use String#split:
String[] tokens = line.split(delimiter);
which will yield empty Strings as well, but you can just ignore those in your constructObjectFromRow function.
I have inherited a Java program which I need to change. In one part of the code, I see I have created over 1000 String variables such as:
String field01
String field02
...
String field1000
I want to make a for loop to set all of the mentioned variables to "", but I am having issues with building a correct format for the for loop.
How do I create field+(i) in the for loop to set field01 to "" and the rest?
A for loop... Well, you could make this an array, but there's not really any way to make this into a for loop without an array.
Here's an example with one:
String[] test = new String[1000];
for (int number; numer < 1000; number++){
test[number] = "";
}
You have to use Reflection for doing the same.
class Test {
String field1
String field2
...
String field1000
}
public class FieldTest {
public static void main(String args[]) {
Test t = new Test();
Class cls = t.getClass();
for(int i=0 ; i<=1000; i++){
Field f1 = cls.getField("field"+i);
f1.set(t, "");
}
}
}
You can't really do this in Java. An alternative is to make a String array where the index of the array is the number of the variable that you want. So field01 would be stored in your string array at index 1.
First, create an array. Second, use Arrays.fill:
String[] fields = new String[1000];
Arrays.fill(fields, "");
Later you can access or modify individual variables by indices like fields[index].
Creating 1000 variables with similar names is not how people program in Java.
I know it is not the exact answer, but may help you.or you will need to use reflection.
Map<String, String> container = new HashMap<String, String>();
for (int i = 0; i <1000; i++) {
container.put("field"+ i, "\"\" ");
}
I have a set of data that look like this.
1:2:3:4:5
6:7:8:9:10
I have manage to use array list to store the information using a delimiter of ":".
However i would like to store the information of their line numbers together in the array list.
class test
{
String items;
String linenumber;
}
Example:
test(1,1)
test(2,1)
test(6,2)
test(7,2)
Here is my current code.
Scanner fileScanner = new Scanner(new File(fname));
fileScanner.useDelimiter("\n");
int counter = 0; String scounter;
String test;
String events;
while(fileScanner.hasNext())
{
events = fileScanner.next();
scounter = Integer.toString(counter);
Base obj = new Base(scounter, events);
baseArrayList.add(obj);
}
fileScanner.close();
I have try using delimiter "\n" and then trying to split out the string and it is not very successful.
Any advice would be appreciated.
public void Base_Seperator()
{
String temp, temp2;
String[] split;
String days, events;
for(int i = 0; i < baseArrayList.size(); i++)
{
temp = baseArrayList.get(i).events;
temp2 = baseArrayList.get(i).days;
split = temp.split(":");
}
}
Despite the code in #Alex's answer that may solve your problem, your attempt is almost close to get what you want/need. Now you only need to create Test instances and store them in a container, usually a List. I'll add the necessary code to start this from your code:
//it is better to return the List instead of declaring it as a static field
public List<Test> Base_Seperator() {
//try to declare variables in the narrower scope
//String temp, temp2;
//String[] split;
//String days, events;
//this variable must be recognized in all the paths of this method
List<Test> testList = new ArrayList<Test>();
for(int i = 0; i < baseArrayList.size(); i++) {
//these variables should only work within the for statement
String temp = baseArrayList.get(i).events;
String temp2 = baseArrayList.get(i).days;
String[] split = temp.split(":");
//you have splitted the String by :
//now you have every element between : as an item stored in split array
//go through each one and create a new Test instance
//first, let's create the lineNumber variable as String
String lineNumber = Integer.toString(i+1);
//using enhanced for to go through these elements
for (String value : split) {
//now, let's create Test instance
Test test = new Test(value, lineNumber);
//store the instance in testList
testList.add(test);
}
}
//now just return the list with the desired values
return testList;
}
Not part of your question, but some advices:
There are plenty other ways to write code to achieve the same solution (take #Alex's answer as an example). I didn't posted any of them because looks like you're in learning phase, so it will be better for you to first achieve what you're looking for with your own effort (and a little of help).
Not sure if you're doing it (or not) but you should not use raw types. This is, you should always provide a generic type when the class/interface needs it. For example, it is better to define a variable as ArrayList<MyClass> myClassList rather than ArrayList myClass so the class become parameterized and the compiler can help you to avoid problems at runtime.
It is better to always program oriented to interfaces/abstract classes. This means, it is better to declare the variables as an interface or abstract class rather than the specific class implementation. This is the case for ArrayList and List:
List<String> stringList = new ArrayList<String>();
//above is better than
ArrayList<String> stringList2 = new ArrayList<String>();
In case you need to use a different implementation of the interface/abstract class, you will have to change the object initialization only (hopefully).
More info:
What is a raw type and why shouldn't we use it?
What does it mean to "program to an interface"?
Looks like you want to store days instead of lineNumber in your Test instances:
//comment this line
//Test test = new Test(value, lineNumber);
//use this one instead
Test test = new Test(value, days);
First of all you don't need to keep line number info in the test object because it can be inferred from the ArrayList that holds them. If you must though, it should be changed to an int. So,
class test
{
ArrayList items<Integer>;
int linenumber;
public test(int line, String[] input){
items=new ArrayList();
linenumber=line;
//populate with the line read by the Scanner
for(int i=0; i<input.lenth; i++)
items.add(Integer.parseInt(input[i]));
}
}
I use an ArrayList inside test because you don't know how many elements you'll be handling. Moving on to the scanner
Scanner fileScanner = new Scanner(new File(fname));
// fileScanner.useDelimiter("\n"); You don't need this!
String tmp[];
int line=0; //number of lines
while(fileScanner.hasNext()) {
line++;
//this returns the entire line, that's why you don't need useDelimeter()
//it also splits it on '.' I'm not sure if that needs to be escaped but
//just to be sure
tmp=fileScanner.nextLine() . split(Pattern.quote("."));
baseArrayList.add(new test(line, tmp));
}
fileScanner.close();
Here I use test to store the objects you read, I'm not sure what Base is supposed to be.
A Java Bean/construct is required that will hold the day and the item together. The following code will read the text file. Each line will be converted to a List where finally the application will populate the List DayItems collection properly.
public class DayItem {
private int day;
private String item;
public int getDay() {
return day;
}
public void setDay(final int day) {
this.day = day;
}
public String getItem() {
return item;
}
public void setItem(final String item) {
this.item = item;
}
}
And main code
public class ReadFile {
private static final List<DayItem> dayItems = new ArrayList<DayItem>();
public static void main(String args[]) throws FileNotFoundException{
final BufferedReader bufferReader = new BufferedReader(new FileReader("items.txt"));
int lineNumber=0;
try
{
String currentLine;
while ((currentLine = bufferReader.readLine()) != null) {
lineNumber++;
List<String> todaysItems = Arrays.asList(currentLine.split(":"));
addItems(todaysItems,lineNumber);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void addItems(final List<String> todaysItems,final int day){
int listSize = todaysItems.size();
for(int i=0;i<listSize;i++){
String item = todaysItems.get(i);
DayItem dayItem = new DayItem();
dayItem.setDay(day);
dayItem.setItem(item);
dayItems.add(dayItem);
}
}
}
This might be a dumb question but its frustrating me. I am adding into my array list a object during a loop but outside of the loop, all of the array list is overwritten with the last element in the array. It goes something like this.
while(fin.hasNextLine())
{
String line = fin.nextLine();
String[] user = line.split(",");
r.add(new User(user[0], user[1]));
System.out.println(r.get(count).getName());
count++;
}
This gives me an output of something like this (USER1, USER2, USER3, etc.) during the loop.
However, right after the loop I now have an output of something like this (USER500, USER500, USER500).
while(fin.hasNextLine())
{
String line = fin.nextLine();
String[] user = line.split(",");
r.add(new User(user[0], user[1]));
System.out.println(r.get(count).getName());
count++;
}
for (int i =0; i < r.size(); i++)
{
System.out.println(r.get(i).getName());
}
I managed to verify that this is the class where I'm having the problem and only one other method uses the array list in this class which i commented out.
I'm going to have to put on my psychic debugging goggles, but I predict tht your User class looks like this:
public class User {
private static String name;
public User(String x, String somethingElse) {
name = x;
}
public String getName() {
return name;
}
}
Note that name is static. Therefore that's one variable - not one per instance of User. You want it to be an instance field, so that each User object has a different name variable.