The csv file looks like this
I'm trying to get the field values like name, it is the value after $$NAME$$ (there is a space after the identifier). How do I store the value for each field by using BufferedReader in Java? The fields could be in any line number and not in a fixed place or format, and also throw out an error if there is any special characters or null value is encountered.
int n = 100; // Max lines
String line;
try (BufferedReader br = new BufferedReader(new FileReader(str)))
{
while ((line = br.readLine()) != null && i++ < n)
{
br.readLine();
line = br.readLine();
System.out.println(line);
}
}
Once the values are extracted from the CSV file, I need to store them in a string variable and use it later to insert into the database for each column values
Case 2:And also for the last field $$GROUP$$ CATEGORY the value is "5" in cell 9 to 11 and i need to match that the column CATEGORY in the database has to be 5 stored in a string to be inserted into the database column of the same name.
The regex wont find the exact match when i used line.matches condition
The following code will read only the first 100 lines of the file and extract the values into a list.
java.nio.file.Path path = java.nio.file.Paths.get(str);
try {
java.util.List<String> values = java.nio.file.Files.lines(path)
.limit(100)
.filter(line -> line.matches("\\$\\$[A-Z]+\\$\\$ [0-9A-Z]*$"))
.map(line -> {
String[] words = line.split(" ");
return words.length == 2 ? words[1] : "";
})
.collect(java.util.stream.Collectors.toList());
System.out.println(values);
}
catch (java.io.IOException xIo) {
xIo.printStackTrace();
}
According to the sample file in your question, the above code will create the following list.
[JOHN, CA, SF, XYZ, , 25, CATEGORY, ]
If you want a Map instead of a List where the Map key is the value between the double $ characters and the Map value is the part after the space, then
Function<String, String> keyMapper = line -> {
String[] parts = line.split(" ");
return parts[0].substring(2, parts[0].length() - 2);
};
Function<String, String> valueMapper = line -> {
String[] parts = line.split(" ");
if (parts.length > 1) {
return parts[1];
}
else {
return "";
}
};
Path path = Paths.get(str);
try {
Map<String, String> map = Files.lines(path)
.limit(100)
.filter(line -> line.matches("\\$\\$[A-Z]+\\$\\$ [0-9A-Z]*$"))
.collect(Collectors.toMap(keyMapper, valueMapper));
System.out.println(map);
}
catch (IOException xIo) {
xIo.printStackTrace();
}
This will create the following Map
{GROUP=CATEGORY, WEATHER=, CITY=SF, STATE=CA, TIME=, NAME=JOHN, REGION=XYZ, AGE=25}
You could use regex here to both detect the name line:
int n = 100; // Max lines
String line;
try (BufferedReader br = new BufferedReader(new FileReader(str))) {
while ((line = br.readLine()) != null && i++ < n) {
if (line.matches("\\$\\$NAME\\$\|$.$")) {
System.out.println(line.split(" ")[1]);
}
}
}
I recommend you first split the line by space, then you'll have something like
$$NAME$$, JOHN. Then retrieve the key between the dollar signs.
An example based on your snippet:
int i = 0;
int n = 100; // Max lines
String line;
try (BufferedReader br = new BufferedReader(new FileReader(str))) {
while ((line = br.readLine()) != null && i++ < n) {
String[] splitLine = line.split(" ");
String key = splitLine[0].split("\\$\\$")[1];
String value = splitLine[1];
System.out.printf("Name: %s | Value: %s%n", key, value);
}
}
You could also use a more modern approach using Java 9 Files API and streams, here's an example of that:
public static void main(String[] args) throws IOException, URISyntaxException {
Path filePathFromProjectFolder = Path.of(ClassLoader.getSystemResource("file.csv").toURI());
Map<String, String> csvValues = Files.readAllLines(filePathFromProjectFolder).stream()
.map(line -> line.split(" "))
.collect(Collectors.toMap(line -> getKeyName(line[0]), line -> line[1]));
System.out.println(csvValues);
}
private static String getKeyName(String str) {
return str.split("\\$\\$")[1];
}
You need to extract data from a CSV file according to the format of “fieldname field value”. The code will be rather long if you try to do this in Java.
Try using SPL, the open-source Java package, to get it done. It is easy with only two lines of code:
A
1
=file("data.csv").read#n().select(left(~,2)=="").(right( ,−2).split("")
2
=create(${A1.(~(1)).concat#c()}).record(A1.(~(2)))
SPL offers JDBC driver to be invoked by Java. Just store the above SPL script as csv2tbl.splx and invoke it in Java as you call a stored procedure:
…
Class.forName("com.esproc.jdbc.InternalDriver");
con= DriverManager.getConnection("jdbc:esproc:local://");
st = con.prepareCall("call csv2tbl()");
st.execute();
…
Related
I have a csv file that doesn't always have the same number of lines. However, I want a method to only read me the last line, so I can access the first column of that last line. So far I haven't found a solution, that does exactly that.
Right now I'm just at the point were I would read every single line with BufferedReader and save it into an Array.
public void readPreviousEntryID(){
String path = "csvs/cartEntries.csv";
try {
BufferedReader br = new BufferedReader((new FileReader(path)));
String line;
while ((line = br.readLine() != null)) {
String[] values = line.split(",");
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
Normally I would then access the first entry of every line by using values[0]. But I just want the first value of the last line.
I thought about counting the number of lines in the while loop by incrementing an integer and then using the final value of that integer to access the corresponding line, but I'm not sure if this would work or how I would implement that.
I hope I included enough information to make the problem understandable. This is my first question here and I'm quite new to Java.
Simply read the lines of the file in a loop and save the values of the last line read. After the loop terminates, values contains the contents of the last line in the file.
public void readPreviousEntryID() throws IOException {
String path = "csvs/cartEntries.csv";
try (FileReader fr = new FileReader(path);
BufferedReader br = new BufferedReader(fr)) {
String[] values = null;
String line = br.readLine();
while (line != null) {
values = line.split(",");
line = br.readLine();
}
if (values == null) {
throw new IOException("File is empty.");
}
// Handle 'values[0]'
}
}
The advantage of the above code is that you don't need to store the entire file contents in the computer memory. If the CSV file is very large, you may get OutOfMemoryError.
Note that is important to close a file after you have finished reading it. Since Java 7 you can use try-with-resources.
Rather than catch the IOException and wrap it in a RuntimeException, I suggest that you simply declare that method readPreviousEntryID may throw an IOException. Refer to Unchecked Exceptions — The Controversy.
It is probably also a good idea to check, after the loop terminates, that values contains the expected number of elements, e.g.
if (values.length == 5) {
// Handle 'values[0]'
}
else {
throw new IOException("Invalid last line.");
}
Edit
Alternatively, no need to split every line. Just save the last line read and split that last line after the loop terminates.
public void readPreviousEntryID() throws IOException {
String path = "csvs/cartEntries.csv";
try (FileReader fr = new FileReader(path);
BufferedReader br = new BufferedReader(fr)) {
String lastLine = null;
String line = br.readLine();
while (line != null) {
lastLine = line;
line = br.readLine();
}
if (lastLine == null) {
throw new IOException("File is empty.");
}
String[] values = lastLine.split(",");
// Handle 'values[0]'
}
}
Why not stored all lines into List and get last line details such as follows
private List<String[]> readLines = new ArrayList<>();
public void readPreviousEntryID(){
String path = "csvs/cartEntries.csv";
try {
String line;
BufferedReader br = new BufferedReader(new FileReader(path));
while ((line = br.readLine()) != null) {
String[] values = line.split(",");
readLines.add(values);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public String[] getLastLine() {
return readLines.get(readLines.size()-1);
}
This above function will gives the last row of csv file.
In Linux one would use the tail command to print the n last lines. Search java tail will get you some implementations.
A good fast implementation for large files would use a RandomAccessFile, maybe a MemoryMappedByteBuffer, and search back from the end for a \n.
In your case you can keep it simple.
public String readPreviousEntryID(){
Path path = Paths.get("csvs/cartEntries.csv");
try (Stream<String> lines = Files.lines(path, Charset.defaultCharset())) {
return lines
.filter(line -> !line.isEmpty())
.reduce("", (acc, line) -> line);
} catch (FileNotFoundException e) {
// You gave a relative path, show the actual full path:
System.out.println("File not found: " + Files.toAbsolutePath());
throw new RuntimeException(e);
} // Automatically closes the Stream lines, even on exception or return.
}
Try-with-resources try (... declarations of AutoCloseables ...) { ... } ensuring the call to .close().
Stream, the newest walk through items. Here skipping empty lines and "accumulating" just the last line. You need not keep all lines in memory.
Lambdas, x -> ... or (x, y) -> ... declare an anonymous function with 1 resp. 2 parameter declarations.
Path is a generalisation of disk file only File. Path can also be from an URL, inside a zip file etcetera.
Files is a worthwile utility class providing many Path related goodies. Files.
Some good Answers have been posted. Here is a variation using streams.
Also, learn about NIO.2 as the modern way to work with files in Java.
Some untested code to try:
Path path = Paths.get( "/csvs" , "cartEntries.csv" ) ;
Optional < String > lastLine =
Files
.lines( path )
.reduce( ( previousLine , currentLine ) -> currentLine ) ;
if( lastLine.isPresent() ) {
String[] parts = lastLine.get().split( "," ) ;
…
}
Or, re-organized into a one-liner:
String[] parts =
Files
.lines(
Paths.get( "/csvs" , "cartEntries.csv" )
)
.reduce(
( previousLine , currentLine ) -> currentLine
)
.map(
line -> line.split.( "," )
)
.orElse(
String[] :: new
)
;
I have a .csv file that is formated like this:
ID,date,itemName
456,1-4-2020,Lemon
345,1-3-2020,Bacon
345,1-4-2020,Sausage
123,1-1-2020,Apple
123,1-2-2020,Pineapple
234,1-2-2020,Beer
345,1-4-2020,Cheese
I have already implemented the algorithm to go through the file, scan for the first number and sort it in a descending order and make a new output:
123,1-1-2020,Apple
123,1-2-2020,Pineapple
234,1-2-2020,Beer
345,1-3-2020,Bacon
345,1-4-2020,Cheese
345,1-4-2020,Sausage
456,1-4-2020,Lemon
My question is, how do I implement my algorithm to make an output that counts the duplicate first number entries and reformat it to make it look like this...
123,1-1-2020,1,Apple
123,1-2-2020,1,Pineapple
234,1-2-2020,1,Beer
345,1-3-2020,1,Bacon
345,1-4-2020,2,Cheese,Sausage
456,1-4-2020,1,Lemon
...so that it counts the number of occurrence for each ID, denote it with the number of times, and if the date of that ID is also the same, combine the item names to the same line. Below is my source code (each line in the .csv is made into an object named 'receipt' that has ID, date, and name with their respective get() methods):
public class ReadFile {
private static List<Receipt> readFile() {
List<Receipt> receipts = new ArrayList<>();
try {
BufferedReader reader = new BufferedReader(new FileReader("dataset.csv"));
// Move past the first title line
reader.readLine();
String line = reader.readLine();
// Start reading from second line till EOF, split each string at ","
while (line != null) {
String[] attributes = line.split(",");
Receipt attribute = getAttributes(attributes);
receipts.add(attribute);
line = reader.readLine();
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return receipts;
}
private static Receipt getAttributes(String[] attributes) {
// Get ID located before the first ","
long memberNumber = Long.parseLong(attributes[0]);
// Get date located after the first ","
String date = attributes[1];
// Get name located after the second ","
String name = attributes[2];
return new Receipt(memberNumber, date, name);
}
// Parse the data into new file after sorting
private static void parse(List<Receipt> receipts) {
PrintWriter output = null;
try {
output = new PrintWriter("output.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// For each receipts, assert the text output stream is not null, print line.
for (Receipt p : receipts) {
assert output != null;
output.println(p.getMemberNumber() + "," + p.getDate() + "," + p.getName());
}
assert output != null;
output.close();
}
// Main method, accept input file, sort and parse
public static void main(String[] args) {
List<Receipt> receipts = readFile();
QuickSort q = new QuickSort();
q.quickSort(receipts);
parse(receipts);
}
}
The easiest way is to use a map.
Sample data from your file.
String[] lines = {
"123,1-1-2020,Apple",
"123,1-2-2020,Pineapple",
"234,1-2-2020,Beer",
"345,1-3-2020,Bacon",
"345,1-4-2020,Cheese",
"345,1-4-2020,Sausage",
"456,1-4-2020,Lemon"};
Create a map
as you read the lines, split them and add them to the map using the compute method. This will put the line in if the key (number and date) doesn't exist. Otherwise it simply appends the last item to the existing entry.
the file does not have to be sorted but the values will be added to the end as they are encountered.
Map<String, String> map = new LinkedHashMap<>();
for (String line : lines) {
String[] vals = line.split(",");
// if v is null, add the line
// if v exists, take the existing line and append the last value
map.compute(vals[0]+vals[1], (k,v)->v == null ? line : v +","+vals[2]);
}
for (String line : map.values()) {
String[] fields = line.split(",",3);
int count = fields[2].split(",").length;
System.out.printf("%s,%s,%s,%s%n", fields[0],fields[1],count,fields[2]);
}
For this sample run prints
123,1-1-2020,1,Apple
123,1-2-2020,1,Pineapple
234,1-2-2020,1,Beer
345,1-3-2020,1,Bacon
345,1-4-2020,2,Cheese,Sausage
456,1-4-2020,1,Lemon
I am trying to write a program that checks two files and prints the common contents from both the files.
Example of the file 1 content would be:
James 1
Cody 2
John 3
Example of the file 2 content would be:
1 Computer Science
2 Chemistry
3 Physics
So the final output printed on the console would be:
James Computer Science
Cody Chemistry
John Physics
Here is what I have so far in my code:
public class Filereader {
public static void main(String[] args) throws Exception {
File file = new File("file.txt");
File file2 = new File("file2.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));
BufferedReader reader2 = new BufferedReader(new FileReader(file2));
String st, st2;
while ((st = reader.readLine()) != null) {
System.out.println(st);
}
while ((st2 = reader2.readLine()) != null) {
System.out.println(st2);
}
reader.close();
reader2.close();
}
}
I am having trouble in figuring out how to match the file contents, and print only the student name and their major by matching the student id in each of the file. Thanks for all the help.
You can use the other answers and make an object to every file, like tables in databases.
public class Person{
Long id;
String name;
//getters and setters
}
public class Course{
Long id;
String name;
//getters and setters
}
Them you have more control with your columns and it is simple to use.
Further you will use an ArrayList<Person> and an ArrayList<Course> and your relation can be a variable inside your objects like courseId in Person class or something else.
if(person.getcourseId() == course.getId()){
...
}
Them if the match is the first number of the files use person.getId() == course.getId().
Ps: Do not use split(" ") in your case, because you can have other objects with two values i.e 1 Computer Science.
What you want is to organize your text file data into map, then merge their data. This will work even if your data are mixed, not in order.
public class Filereader {
public static void main(String[] args) throws Exception {
File file = new File("file.txt");
File file2 = new File("file2.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));
BufferedReader reader2 = new BufferedReader(new FileReader(file2));
String st, st2;
Map<Integer, String> nameMap = new LinkedHashMap<>();
Map<Integer, String> majorMap = new LinkedHashMap<>();
while ((st = reader.readLine()) != null) {
System.out.println(st);
String[] parts = st.split(" "); // Here you got ["James", "1"]
String name = parts[0];
Integer id = Integer.parseInt(parts[1]);
nameMap.put(id, name);
}
while ((st2 = reader2.readLine()) != null) {
System.out.println(st2);
String[] parts = st2.split(" ");
String name = parts[1];
Integer id = Integer.parseInt(parts[0]);
majorMap.put(id, name);
}
reader.close();
reader2.close();
// Combine and print
nameMap.keySet().stream().forEach(id -> {
System.out.println(nameMap.get(id) + " " + majorMap.get(id));
})
}
}
You should read these files at the same time in sequence. This is easy to accomplish with a single while statement.
while ((st = reader.readLine()) != null && (st2 = reader2.readLine()) != null) {
// print both st and st2
}
The way your code is written now, it reads one file at a time, printing data to the console from each individual file. If you want to meld the results together, you have to combine the output of the files in a single loop.
Given that the intention may also be that you have an odd-sized file in one batch but you do have numbers to correlate across, or the numbers may come in a nonsequential order, you may want to store these results into a data structure instead, like a List, since you know the specific index of each of these values and know where they should fit in.
Combining the NIO Files and Stream API, it's a little simpler:
public static void main(String[] args) throws Exception {
Map<String, List<String[]>> f1 = Files
.lines(Paths.get("file1"))
.map(line -> line.split(" "))
.collect(Collectors.groupingBy(arr -> arr[1]));
Map<String, List<String[]>> f2 = Files
.lines(Paths.get("file2"))
.map(line -> line.split(" "))
.collect(Collectors.groupingBy(arr -> arr[0]));
Stream.concat(f1.keySet().stream(), f2.keySet().stream())
.distinct()
.map(key -> f1.get(key).get(0)[0] + " " + f2.get(key).get(0)[1])
.forEach(System.out::println);
}
As can easily be noticed in the code, there are assumptions of valid data an of consistency between the two files. If this doesn't hold, you may need to first run a filter to exclude entries missing in either file:
Stream.concat(f1.keySet().stream(), f2.keySet().stream())
.filter(key -> f1.containsKey(key) && f2.containsKey(key))
.distinct()
...
If you change the order such that the number comes first in both files, you can read both files into a HashMap then create a Set of common keys. Then loop through the set of common keys and grab the associated value from each Hashmap to print:
My solution is verbose but I wrote it that way so that you can see exactly what's happening.
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import java.io.File;
import java.util.Scanner;
class J {
public static Map<String, String> fileToMap(File file) throws Exception {
// TODO - Make sure the file exists before opening it
// Scans the input file
Scanner scanner = new Scanner(file);
// Create the map
Map<String, String> map = new HashMap<>();
String line;
String name;
String code;
String[] parts = new String[2];
// Scan line by line
while (scanner.hasNextLine()) {
// Get next line
line = scanner.nextLine();
// TODO - Make sure the string has at least 1 space
// Split line by index of first space found
parts = line.split(" ", line.indexOf(' ') - 1);
// Get the class code and string val
code = parts[0];
name = parts[1];
// Insert into map
map.put(code, name);
}
// Close input stream
scanner.close();
// Give the map back
return map;
}
public static Set<String> commonKeys(Map<String, String> nameMap,
Map<String, String> classMap) {
Set<String> commonSet = new HashSet<>();
// Get a set of keys for both maps
Set<String> nameSet = nameMap.keySet();
Set<String> classSet = classMap.keySet();
// Loop through one set
for (String key : nameSet) {
// Make sure the other set has it
if (classSet.contains(key)) {
commonSet.add(key);
}
}
return commonSet;
}
public static Map<String, String> joinByKey(Map<String, String> namesMap,
Map<String, String> classMap,
Set<String> commonKeys) {
Map<String, String> map = new HashMap<String, String>();
// Loop through common keys
for (String key : commonKeys) {
// TODO - check for nulls if get() returns nothing
// Fetch the associated value from each map
map.put(namesMap.get(key), classMap.get(key));
}
return map;
}
public static void main(String[] args) throws Exception {
// Surround in try catch
File names = new File("names.txt");
File classes = new File("classes.txt");
Map<String, String> nameMap = fileToMap(names);
Map<String, String> classMap = fileToMap(classes);
Set<String> commonKeys = commonKeys(nameMap, classMap);
Map<String, String> nameToClass = joinByKey(nameMap, classMap, commonKeys);
System.out.println(nameToClass);
}
}
names.txt
1 James
2 Cody
3 John
5 Max
classes.txt
1 Computer Science
2 Chemistry
3 Physics
4 Biology
Output:
{Cody=Chemistry, James=Computer, John=Physics}
Notes:
I added keys in classes.txt and names.txt that purposely did not match so you see that it does not come up in the output. That is because the key never makes it into the commonKeys set. So, they never get inserted into the joined map.
You can loop through the HashMap if you want my calling map.entrySet()
I have a text file (collection of all valid english words) from a github project that looks like this words.txt
My text file is under the resources folder in my project.
I have also a list of rows obtained from a table in mysql.
What i'm trying to do is to check if all the words in a every row are valid english words, that's why I compare each row with the words contained in my file.
This what i've tried so far :
public static void englishCheck(List<String> rows) throws IOException {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
int lenght, occurancy = 0;
for ( String row : rows ){
File file = new File(classLoader.getResource("words.txt").getFile());
lenght = 0;
if ( !row.isEmpty() ){
System.out.println("the row : "+row);
String[] tokens = row.split("\\W+");
lenght = tokens.length;
for (String token : tokens) {
occurancy = 0;
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
while ((line = br.readLine()) != null ){
if ((line.trim().toLowerCase()).equals(token.trim().toLowerCase())){
occurancy ++ ;
}
if (occurancy == lenght ){ System.out.println(" this is english "+row);break;}
}
}
}
}
}
this works only for the very first rows, after that my method loops over the rows only displaying them and ignores the comparison, I would like to know why this isn't working for my set of rows, It works also if I predefined my list like this List<String> raws = Arrays.asList(raw1, raw2, raw3 ) and so on
You can use the method List#containsAll(Collection)
Returns true if this list contains all of the elements of the
specified collection.
lets assume you have both list flled myListFromRessources and myListFromRessources then you can do:
List<String> myListFromRessources = Arrays.asList("A", "B", "C", "D");
List<String> myListFromRessources = Arrays.asList("D", "B");
boolean myInter = myListFromRessources.containsAll(myListFromSQL);
System.out.println(myInter);
myListFromSQL = Arrays.asList("D", "B", "Y");
myInter = myListFromRessources.containsAll(myListFromSQL);
System.out.println(myInter);
You can read words.txt file, convert words into lower case, then put words into HashSet.
Use the boolean contains(Object o) or boolean containsAll(Collection<?> c); methods to compare each word.
The time was O(n).
TIP: Do not read file in every loop. Reading file is very very slow.
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("words.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
List<String> wordList = new LinkedList<String>(); // You do not know word count, LinkedList is a better way.
String line = null;
while ((line = reader.readLine()) != null) {
String[] words = line.toLowerCase().split("\\W+");
wordList.addAll(Arrays.asList(words));
}
Set<String> wordSet = new HashSet<String>(wordList.size());
wordSet.addAll(wordList);
// then you can use the wordSet to check.
// You shold convert the tokens to lower case.
String[] tokens = row.toLowerCase().split("\\W+");
wordSet.containsAll(Arrays.asList(tokens));
The reason your code doesn't work is that occurancy can never be anything other than 0 or 1. You can see that by following the logic or going through a debugger.
If your words.txt file is not too large, and you have enough RAM available, you can speed up processing by reading the words.txt file into memory at the start. Also, you only ever need to call toLowerCase() once, instead of every time you compare. However, be careful with locales. The following code should work as long as you haven't got any non-English characters such as a German eszett or a Greek sigma.
public static void englishCheck(List<String> rows) throws IOException {
final URI wordsUri;
try {
wordsUri = ClassLoader.getSystemResource("words.txt").toURI();
} catch (URISyntaxException e) {
throw new AssertionError(e); // can never happen
}
final Set<String> words = Files.lines(Paths.get(wordsUri))
.map(String::toLowerCase)
.collect(Collectors.toSet());
for (String row: rows)
if (!row.isEmpty()) {
System.out.println("the row : " + row);
String[] tokens = row.toLowerCase().split("\\W+");
if (words.containsAll(Arrays.asList(tokens)))
System.out.println(" this is english " + row);
}
}
I have a text file with 300 lines or so. And the format is like:
Name Amount Unit CountOfOrder
A 1 ml 5000
B 1 mgm 4500
C 4 gm 4200
// more data
I need to read the text file line by line because each line of data should be together for further processing.
Now I just use string array for each line and access the data by index.
for each line in file:
array[0] = {data from the 'Name' column}
array[1] = {data from the 'Amount' column}
array[2] = {data from the 'Unit' column}
array[3] = {data from the 'CountOfOrder' column}
....
someOtherMethods(array);
....
However, I realized that if the text file changes its format (e.g. switch two columns, or insert another column), it would break my program (accessing through index might be wrong or even cause exception).
So I would like to use the title as reference to access each column. Maybe HashMap is a good option, but since I have to keep each line of data together, if I build a HashMap for each line, that would be too expensive.
Does anyone have any thought on this? Please help!
you only need a single hash map to map your column names to the proper column index. you fill the arrays by indexing with integers as you did before, to retrieve a column by name you'd use array[hashmap.get("Amount")].
You can read the file using opencsv.
CSVReader reader = new CSVReader(new FileReader("yourfile.txt"), '\t');
List<String[]> lines = reader.readAll();
The fist line contains the headers.
you can read each line of the file and assuming that the first line of the file has the column header you can parse that line to get all the names of the columns.
String[] column_headers = firstline.split("\t");
This will give you the name of all the columns now you just read through splitting on tabs and they will all line up.
You could do something like this:
BufferedReader in = new BufferedReader(new InputStreamReader(
new FileInputStream(FILE)));
String line = null;
String[] headers = null;
String[] data = null;
Map<String, List<String>> contents = new HashMap<String, List<String>>();
if ((line = in.readLine()) != null) {
headers = line.split("\t");
}
for(String h : headers){
contents.put(h, new ArrayList<String>());
}
while ((line = in.readLine()) != null) {
data = line.split("\t");
if(data.length != headers.length){
throw new Exception();
}
for(int i = 0; i < data.length; i++){
contents.get(headers[i]).add(data[i]);
}
}
It would give you flexibility, and would only require making the map once. You can then get the data lists from the map, so it should be a convenient data structure for the rest of your program to use.
This will give you individual list of columns.
public static void main(String args[]) throws FileNotFoundException, IOException {
List<String> headerList = new ArrayList<String>();
List<String> column1 = new ArrayList<String>();
List<String> column2 = new ArrayList<String>();
List<String> column3 = new ArrayList<String>();
List<String> column4 = new ArrayList<String>();
int lineCount=0;
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
try {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
String tokens[];
while (line != null) {
tokens = line.split("\t");
if(lineCount != 0)
{
int count = 0;
column1.add(tokens[count]); ++count;
column2.add(tokens[count]); ++count;
column3.add(tokens[count]); ++count;
column4.add(tokens[count]); ++count;
continue;
}
if(lineCount==0){
for(int count=0; count<tokens.length; count++){
headerList.add(tokens[count]);
lineCount++;
}
}
}
} catch (IOException e) {
} finally {
br.close();
}
}
using standard java.util.Scanner
String aa = " asd 9 1 3 \n d -1 4 2";
Scanner ss = new Scanner(aa);
ss.useDelimiter("\n");
while ( ss.hasNext()){
String line = ss.next();
Scanner fs = new Scanner(line);
System.out.println( "1>"+ fs.next()+" " +fs.nextInt() +" " +fs.nextLong()+" " +fs.nextBigDecimal());
}
using a bunch of hashmap's is ok...i won't be afraid ;)
if you need to process a lot of data...then try to translate your problem into a dataprocessing transformation
for example:
read all of you data into a hashmap's, but store them in a database using some JPA implementation....then you can go round'a'round your data ;)\