Converting an CSV file to a JSON object in Java - java

Is there an open source java library to convert a CSV (or XLS) file to a JSON object?
I tried using json.cdl, but somehow it does not seem to work for large CSV strings.
I'm trying to find something like http://www.cparker15.com/code/utilities/csv-to-json/, but written in Java.

You can use Open CSV to map CSV to a Java Bean, and then use JAXB to convert the Java Bean into a JSON object.
http://opencsv.sourceforge.net/#javabean-integration
http://jaxb.java.net/guide/Mapping_your_favorite_class.html

Here is my Java program and hope somebody finds it useful.
Format needs to be like this:
"SYMBOL,DATE,CLOSE_PRICE,OPEN_PRICE,HIGH_PRICE,LOW_PRICE,VOLUME,ADJ_CLOSE
AAIT,2015-02-26 00:00:00.000,-35.152,0,35.152,35.12,679,0
AAL,2015-02-26 00:00:00.000,49.35,50.38,50.38,49.02,7572135,0"
First line is the column headers. No quotation marks anywhere. Separate with commas and not semicolons. You get the deal.
/* Summary: Converts a CSV file to a JSON file.*/
//import java.util.*;
import java.io.*;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
public class CSVtoJSON extends JFrame{
private static final long serialVersionUID = 1L;
private static File CSVFile;
private static BufferedReader read;
private static BufferedWriter write;
public CSVtoJSON(){
FileNameExtensionFilter filter = new FileNameExtensionFilter("comma separated values", "csv");
JFileChooser choice = new JFileChooser();
choice.setFileFilter(filter); //limit the files displayed
int option = choice.showOpenDialog(this);
if (option == JFileChooser.APPROVE_OPTION) {
CSVFile = choice.getSelectedFile();
}
else{
JOptionPane.showMessageDialog(this, "Did not select file. Program will exit.", "System Dialog", JOptionPane.PLAIN_MESSAGE);
System.exit(1);
}
}
public static void main(String args[]){
CSVtoJSON parse = new CSVtoJSON();
parse.convert();
System.exit(0);
}
private void convert(){
/*Converts a .csv file to .json. Assumes first line is header with columns*/
try {
read = new BufferedReader(new FileReader(CSVFile));
String outputName = CSVFile.toString().substring(0,
CSVFile.toString().lastIndexOf(".")) + ".json";
write = new BufferedWriter(new FileWriter(new File(outputName)));
String line;
String columns[]; //contains column names
int num_cols;
String tokens[];
int progress = 0; //check progress
//initialize columns
line = read.readLine();
columns = line.split(",");
num_cols = columns.length;
write.write("["); //begin file as array
line = read.readLine();
while(true) {
tokens = line.split(",");
if (tokens.length == num_cols){ //if number columns equal to number entries
write.write("{");
for (int k = 0; k < num_cols; ++k){ //for each column
if (tokens[k].matches("^-?[0-9]*\\.?[0-9]*$")){ //if a number
write.write("\"" + columns[k] + "\": " + tokens[k]);
if (k < num_cols - 1) write.write(", "); }
else { //if a string
write.write("\"" + columns[k] + "\": \"" + tokens[k] + "\"");
if (k < num_cols - 1) write.write(", ");
}
}
++progress; //progress update
if (progress % 10000 == 0) System.out.println(progress); //print progress
if((line = read.readLine()) != null){//if not last line
write.write("},");
write.newLine();
}
else{
write.write("}]");//if last line
write.newLine();
break;
}
}
else{
//line = read.readLine(); //read next line if wish to continue parsing despite error
JOptionPane.showMessageDialog(this, "ERROR: Formatting error line " + (progress + 2)
+ ". Failed to parse.",
"System Dialog", JOptionPane.PLAIN_MESSAGE);
System.exit(-1); //error message
}
}
JOptionPane.showMessageDialog(this, "File converted successfully to " + outputName,
"System Dialog", JOptionPane.PLAIN_MESSAGE);
write.close();
read.close();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Requires Swing but comes with a nifty little GUI so those who know absolutely no Java can use it once packaged into an executable .jar. Feel free to improve upon it. Thank you StackOverflow for helping me out all these years.

#Mouscellaneous basically answered this for you so please give him the credit.
Here is what I came up with:
package edu.apollogrp.csvtojson;
import au.com.bytecode.opencsv.bean.CsvToBean;
import au.com.bytecode.opencsv.bean.HeaderColumnNameMappingStrategy;
import org.codehaus.jackson.map.ObjectMapper;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
public class ConvertCsvToJson {
public static void main(String[] args) throws IOException, ClassNotFoundException {
if (args.length > 1) {
String pathToCsvFile = args[0];
String javaBeanClassName = "edu.apollogrp.csvtojson.bean." + args[1];
final File file = new File(pathToCsvFile);
if (!file.exists()) {
System.out.println("The file you specified does not exist. path=" + pathToCsvFile);
}
Class<?> type = null;
try {
type = Class.forName(javaBeanClassName);
} catch (ClassNotFoundException e) {
System.out.println("The java bean you specified does not exist. className=" + javaBeanClassName);
}
HeaderColumnNameMappingStrategy strat = new HeaderColumnNameMappingStrategy();
strat.setType(type);
CsvToBean csv = new CsvToBean();
List list = csv.parse(strat, new InputStreamReader(new FileInputStream(file)));
System.out.println(new ObjectMapper().writeValueAsString(list));
} else {
System.out.println("Please specify the path to the csv file.");
}
}
}
I used maven to include the dependencies, but you could also download them manually and include them in your classpath.
<dependency>
<groupId>net.sf.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.12</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.12</version>
</dependency>

I have used excel file in this code.you can use csv.
i have wrote this class for particular Excel/csv format which is known to me.
import java.io.File;
public class ReadExcel {
private String inputFile;
public void setInputFile(String inputFile) {
this.inputFile = inputFile;
}
public void read() throws IOException {
File inputWorkbook = new File(inputFile);
Workbook w;
try {
w = Workbook.getWorkbook(inputWorkbook);
// Get the first sheet
Sheet sheet = w.getSheet(0);
// Loop over first 10 column and lines
int columns = sheet.getColumns();
int rows = sheet.getRows();
ContactList clist = new ContactList();
ArrayList<Contact> contacts = new ArrayList<Contact>();
for (int j = 1; j < rows; j++) {
Contact contact = new Contact();
for (int i = 0; i < columns; i++) {
Cell cell = sheet.getCell(i, j);
switch (i) {
case 0:
if (!cell.getContents().equalsIgnoreCase("")) {
contact.setSrNo(Integer.parseInt(cell.getContents()));
} else {
contact.setSrNo(j);
}
break;
case 1:
contact.setName(cell.getContents());
break;
case 2:
contact.setAddress(cell.getContents());
break;
case 3:
contact.setCity(cell.getContents());
break;
case 4:
contact.setContactNo(cell.getContents());
break;
case 5:
contact.setCategory(cell.getContents());
break;
}
}
contacts.add(contact);
}
System.out.println("done");
clist.setContactList(contacts);
JSONObject jsonlist = new JSONObject(clist);
File f = new File("/home/vishal/Downloads/database.txt");
FileOutputStream fos = new FileOutputStream(f, true);
PrintStream ps = new PrintStream(fos);
ps.append(jsonlist.toString());
} catch (BiffException e) {
e.printStackTrace();
System.out.println("error");
}
}
public static void main(String[] args) throws IOException {
ReadExcel test = new ReadExcel();
test.setInputFile("/home/vishal/Downloads/database.xls");
test.read();
}
}
i have used jxl.jar for excel reading

If your CSV is simple, then this is easy to write by hand - but CSV can include nasty edge cases with quoting, missing values, etc.
load the file using BufferedReader.readLine()
use String.split(",") to get the value from each line - NB this approach will only work correctly if your values don't have commas in!
write each value to the output using BufferedWriter
with the necessary JSON braces and quoting
You might want to use a CSV library, then convert to JSON 'by hand'

Here is a class I generated to return JSONArray, not just to print to a file.
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import org.json.simple.JSONArray;
import org.json.simple.parser.JSONParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.List;
import java.util.Map;
public class CsvToJson {
private static final Logger log = LoggerFactory.getLogger(UtilsFormat.class);
private static CsvToJson instance;
public static JSONArray convert(File input) throws Exception {
JSONParser parser = new JSONParser();
CsvSchema csvSchema = CsvSchema.builder().setUseHeader(true).build();
CsvMapper csvMapper = new CsvMapper();
// Read data from CSV file
List<? extends Object> readAll = csvMapper.readerFor(Map.class).with(csvSchema).readValues(input).readAll();
ObjectMapper mapper = new ObjectMapper();
JSONArray jsonObject = (JSONArray) parser.parse(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(readAll));
System.out.print(jsonObject.toString());
return new JSONArray();
}
}

With Java 8, writing JSON is at hand.
You didn't specify what JSON API you want, so I assume by "JSON object" you mean a string with a serialized JSON object.
What I did in the CSV Cruncher project:
Load the CSV using HSQLDB. That's a relatively small (~2 MB) library, which actually implements a SQL 2008 database.
Query that database using JDBC.
Build a JDK JSON object (javax.json.JsonObject) and serialize it.
Here's how to do it:
static void convertResultToJson(ResultSet resultSet, Path destFile, boolean printAsArray)
{
OutputStream outS = new BufferedOutputStream(new FileOutputStream(destFile.toFile()));
Writer outW = new OutputStreamWriter(outS, StandardCharsets.UTF_8);
// javax.json way
JsonObjectBuilder builder = Json.createObjectBuilder();
// Columns
for (int colIndex = 1; colIndex <= metaData.getColumnCount(); colIndex++) {
addTheRightTypeToJavaxJsonBuilder(resultSet, colIndex, builder);
}
JsonObject jsonObject = builder.build();
JsonWriter writer = Json.createWriter(outW);
writer.writeObject(jsonObject);
The whole implementation is here. (Originally I wrote my own CSV parsing and JSON writing, but figured out both are complicated enough to reach for a tested off-the-shelf library.)

If you're using Java 8, you can do something like this. No Libraries or complicated logic required.
Firstly, create a POJO representing your new JSON object. In my example it's called 'YourJSONObject' and has a constructor taking two strings.
What the code does is initially reads the file, then creates a stream of String based lines. ( a line is equivalent to a line in your CSV file).
We then pass the line in to the map function which splits it on a comma and then creates the YourJSONObject.
All of these objects are then collected to a list which we pass in to the JSONArray constructor.
You now have an Array of JSONObjects. You can then call toString() on this object if you want to see the text representation of this.
JSONArray objects = new JSONArray(Files.readAllLines(Paths.get("src/main/resources/your_csv_file.csv"))
.stream()
.map(s -> new YourJSONObject(s.split(",")[0], s.split(",")[1]))
.collect(toList()));

Old post but I thought I'd share my own solution. It assumes quotations are used around an in-value comma. It also removes all quotations afterwards.
This method accepts a String in CSV format. So it assumes you've already read the CSV file to a string. Make sure you didn't remove the NextLine characters ('\n') while reading.
This method in no way perfect, but it might be the quick one-method solution in pure java you are looking for.
public String CSVtoJSON(String output) {
String[] lines = output.split("\n");
StringBuilder builder = new StringBuilder();
builder.append('[');
String[] headers = new String[0];
//CSV TO JSON
for (int i = 0; i < lines.length; i++) {
String[] values = lines[i].replaceAll("\"", "").split("۞");
if (i == 0) //INDEX LIST
{
headers = values;
} else {
builder.append('{');
for (int j = 0; j < values.length && j < headers.length; j++) {
String jsonvalue = "\"" + headers[j] + "\":\"" + values[j] + "\"";
if (j != values.length - 1) { //if not last value of values...
jsonvalue += ',';
}
builder.append(jsonvalue);
}
builder.append('}');
if (i != lines.length - 1) {
builder.append(',');
}
}
}
builder.append(']');
output = builder.toString();
return output;
}

Related

Formatting Output to CSV File Format to Account for Empty Values In Some Rows

tl;dr: I want to output my HashMap in the same CSV file format that I am reading it in.
I want to preface this with: I may be going about this the wrong way. Because I started this thinking one thing and didn't account for this problem I'm having. So I'm trying to make a small application that will give you a random movie to watch depending on what genre you are in the mood for (similarly to Netflix's Max application, but considerably dumbed down). I have a list of movies that I'm going to format myself in CSV format, because I recently wrote some code that reads in values from a CSV file and I didn't have much to alter.
Here is the dilemma: I have read in the CSV formatted file (only a two line sample file), since I know what columns contain what I use a BufferedReader to read in line by line storing each value of the delimited value in its own ArrayList (I know there is a better way but this is what I came up with for now) I then store each ArrayList according to genre into a HashMap. Now I want to be able to write back out to the same file at some point to edit it. So movies that have been watched will be removed from the HashMap and then overwrite the file, so that when it's read back in next time the movie that was already watched will not be in the file anymore. So the difficulty I am having at this point is formatting the output back out to account for empty spaces in the actual CSV.
So for example, the test file I have only contains two lines where each movie genre has two movies except for drama and comedy. So the file looks like this
Action,Drama,Sci-fi/Fantasy,Thriller/Suspense,Comedy
Action2,,Sci-fi/Fantasy2,Thriller/Suspense2,
Just to solidify what output I want/expected; Say I watch Sci-fi/Fantasy2, it was a good movie, but it's gotta go, the output I want once removed is this
Action,Drama,Sci-fi/Fantasy,Thriller/Suspense,Comedy
Action2,,,Thriller/Suspense2,
But I know I'm not going to get those results because when I simply read the file then output it back out I get:
Action,Action2,
Drama,,
Thriller/Suspense,Thriller/Suspense2,
Comedy,,
Sci-fi/Fantasy,Sci-fi/Fantasy2,
So after getting these results I now realize I didn't plan well enough, and wonder if I'm going about this the wrong way. Does anyone know how to format in the way I described? I tried to find a solution, but after coming up empty handed I deduced that maybe the format I want goes against how a CSV should look, considering some cells in the file would have to be blank. I tried keeping any blank spaces from the file so they would go into the hashmap, but results were the same. I thought about bypassing the output file altogether, but I'm not sure how to save the values I originally read in that go into my map. Any ideas or solutions would greatly appreciated. Here is the class that does all the work:
package rngesus;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
public class ReadWrite {
private static final String[] GENRES = { "Action", "Drama",
"Sci-Fi/Fantasy", "Thriller/Suspense", "Comedy" };
private static final String NEW_LINE = "\n";
private static final String DELIMITER = ",";
private static final int NUM_OF_COL = 5;
private static final int GENRE_1 = 0;
private static final int GENRE_2 = 1;
private static final int GENRE_3 = 2;
private static final int GENRE_4 = 3;
private static final int GENRE_5 = 4;
private static String moviesFile;
private HashMap<String, ArrayList<String>> moviesByGenre;
ArrayList<String> actionMovies;
ArrayList<String> dramaMovies;
ArrayList<String> sciFiFantasyMovies;
ArrayList<String> thrillerSuspenseMovies;
ArrayList<String> comedyMovies;
public ReadWrite() {
moviesFile = "";
moviesByGenre = new HashMap<String, ArrayList<String>>();
actionMovies = new ArrayList<String>();
dramaMovies = new ArrayList<String>();
sciFiFantasyMovies = new ArrayList<String>();
thrillerSuspenseMovies = new ArrayList<String>();
comedyMovies = new ArrayList<String>();
}
public void readAndSortInputFile(String fileOfMovies) throws IOException {
try {
BufferedReader buffRdr = new BufferedReader(new FileReader(
new File(fileOfMovies)));
String line = "";
while ((line = buffRdr.readLine()) != null) {
String[] lnPtr = line.split(",", NUM_OF_COL);
int diff = Math.min(lnPtr.length, NUM_OF_COL);
for (int i = 0; i < diff; i++) {
if ((i == GENRE_1) && !lnPtr[i].isEmpty()) {
actionMovies.add(lnPtr[i]);
} else if ((i == GENRE_2) && !lnPtr[i].isEmpty()) {
dramaMovies.add(lnPtr[i]);
} else if ((i == GENRE_3) && !lnPtr[i].isEmpty()) {
sciFiFantasyMovies.add(lnPtr[i]);
} else if ((i == GENRE_4) && !lnPtr[i].isEmpty()) {
thrillerSuspenseMovies.add(lnPtr[i]);
} else if ((i == GENRE_5) && !lnPtr[i].isEmpty()){
comedyMovies.add(lnPtr[i]);
}
}
}
buffRdr.close();
moviesFile = fileOfMovies;
} catch (FileNotFoundException err) {
err.printStackTrace();
System.out.println("Error: Unable to locate file specified");
}
}
public void mapMoviesToGenre(){
moviesByGenre.put(GENRES[GENRE_1], actionMovies);
moviesByGenre.put(GENRES[GENRE_2], dramaMovies);
moviesByGenre.put(GENRES[GENRE_3], sciFiFantasyMovies);
moviesByGenre.put(GENRES[GENRE_4], thrillerSuspenseMovies);
moviesByGenre.put(GENRES[GENRE_5], comedyMovies);
}
public void initMapToOutput() {
mapMoviesToGenre();
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter(moviesFile);
fileWriter.append(NEW_LINE);
for(ArrayList<String> moviesInGenre : moviesByGenre.values()){
for(String movie : moviesInGenre){
fileWriter.append(movie);
fileWriter.append(DELIMITER);
}
fileWriter.append(NEW_LINE);
}
} catch (Exception err) {
err.printStackTrace();
} finally {
try {
fileWriter.flush();
fileWriter.close();
} catch (IOException err) {
err.printStackTrace();
}
}
}
}
I don't think that is formatted very well but I have some ignorance on that, to anyone, feel free to edit away. I'm already aware that there must be a better way to store the values read in from the file, but for now I'm focused on solving this other dilemma. But if anyone is willing to point anything else out feel free.
Okay, so I worked through it, to whom it may concern. I went ahead and grouped the movies line by line as opposed as to column by column. All I had to do was change the while part of my code to this
int index = 0;
while ((line = buffRdr.readLine()) != null) {
String[] lnPtr = line.split(",", NUM_OF_COL);
int diff = Math.min(lnPtr.length, NUM_OF_COL);
for (int i = 0; i < diff; i++) {
if ((index == GENRE_1) && !lnPtr[i].isEmpty()) {
actionMovies.add(lnPtr[i]);
} else if ((index == GENRE_2) && !lnPtr[i].isEmpty()) {
dramaMovies.add(lnPtr[i]);
} else if ((index == GENRE_3) && !lnPtr[i].isEmpty()) {
sciFiFantasyMovies.add(lnPtr[i]);
} else if ((index == GENRE_4) && !lnPtr[i].isEmpty()) {
thrillerSuspenseMovies.add(lnPtr[i]);
} else if ((index == GENRE_5) && !lnPtr[i].isEmpty()){
comedyMovies.add(lnPtr[i]);
}
}
index++;
}
And I now get movies of the same genre on the same line every time, although sometimes in different order, which I assume is due to going into and out of the HashMap. If anyone knows a cleaner more efficient way to do this, feel free to share, but I solved my problem for now.

search range data on CSV file using java

I'm trying to get selective data from .csv file using java.
for example:
CSV file contains:
Blue, 03/11/2014, 13:00, 10
pink, 04/11/2014, 14:00, 15
Red, 03/11/2014, 15:00, 50
I want to create a program in java which will allow users to select what info they want from that file.
I've been working on the example below but only able to print strings and not the dates/intergers:
package csv;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;
import java.util.Scanner;
public class ReadCVS {
public static void main(String[] args) {
ReadCVS obj = new ReadCVS();
obj.run();
}
public void run() {
String csvFile = "GeoIPCountryWhois.csv";
BufferedReader br = null;
String line = "";
String cvsSplitBy = ",";
File file = new File("GeoIPCountryWhois.csv");
Scanner in = null;
try {
br = new BufferedReader(new FileReader(csvFile));
while ((line = br.readLine()) != null) {
// use comma as separator
String[] colour = line.split(cvsSplitBy);
//System.out.println(country[0]+ country[1] + country[2]+ country[3]);
for (int i = 0; i < colour.length; i++) {
if (colour[i].equals("Pressure")) {
System.out.println(colour[0] + colour[1] + colour[2] + colour[3]); //Matching the string and printing.
}
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("Done");
}
}
Any help/tips will be appreciated!
thanks
I would recommend using 3rd party library for parsing the csv file. This lets you focus on the essence of what you are trying to do, instead of getting hung up on file parsing. Have a look at, for example Apache Commons CSV. This would let your code look like this:
Reader in = new FileReader("GeoIPCountryWhois.csv");
DateFormat df = new SimpleDateFormat("MM/dd/yyyy");
Iterable<CSVRecord> records = CSVFormat.EXCEL.parse(in);
for (CSVRecord record : records) {
String colour = record.get(0);
Date date = df.parse(record.get(1));
String timeString = record.get(2);
Integer value = Integer.parseInt(record.get(3));
// do what you want with the values here.
}
Notice how this does some additional parsing, such as parsing the Date and the Integer. This will let you more easily filter those columns because you can do comparisons.
If you don't want third party dependencies, you could do something similar to what you have:
DateFormat df = new SimpleDateFormat("MM/dd/yyyy");
while ((line = br.readLine()) != null) {
// use comma as separator
String[] columns = line.split(cvsSplitBy);
// extract the columns.
String colour = columns[0].trim();
Date date = df.parse(columns[1].trim());
String time = columns[2].trim();
Integer otherValue = Integer.parseInt(columns[3].trim());
// filter on the colour column.
if(colour.equals("Red")) {
System.out.printf("colour = %s, date = %s, time = %s, val = %d\n", colour, df.format(date), time, otherValue);
}
}
Note how this code is calling String.trim() on all of the columns. This is in case there is extra whitespace around the column after splitting the line. For example, "a, b, c".split(",") would result in the String array {"a", " b", " c"} which has an extra space in " b" and " c". This is probably the source of the bug you have where you can only filter on the first column.
As a third option, which is sort of overkill, you could use CsvJdbc. This provides a JDBC interface to sql files, which you can then execute SQL queries over. I've never used this library but it looks interesting. Given that you are trying to filter CSV files.

Using a loop I want to find the value of a column from my CSV file java

Forgive me if this is a basic (or not very well explained) question, I am fairly new to Java and have been reading extensive material as well as trying to understand the relevant Javadoc but to no avail.
To give a brief background as to what I am trying to create, I have created a reader class which reads data in from a csv file (4 lines long) including fields such as Item ID, price, description etc. I have created a separate demo class that displays the details of this csv file (through creating an instance of my reader class) and am now trying to create a method that asks the user to input an Item ID that then displays the corresponding Item, based on the ID input by the user. The part I am stuck on is accessing specific rows/columns in a csv file and then comparing these with a given string (entered by the user which corresponds to a specific field in the csv file)
This is what I have come up with thus far:
input = new Scanner(System.in);
System.out.println("Enter a product code");
String prodC = input.next();
//Here I want to know if there is a way of accessing a field in a csv file
Any ideas would be greatly appreciated.
UPDATE
Thank you for quick responses, am currently reading through and seeing how I can try to implement the various techniques. In response to the comment asking about the file reader, this is how I have set that out:
public CatalogueReader(String filename) throws FileNotFoundException {
this.filename = filename;
this.catalogue = new Catalogue();
Scanner csvFile;
try {
csvFile = new Scanner(new File(filename));
} catch (FileNotFoundException fnf) {
throw new FileNotFoundException("File has not been found!");
}
csvFile.useDelimiter("\n");
boolean first = true;
String productCode;
double price;
String description;
double weight;
int rating;
String category;
boolean ageRestriction;
String csvRows;
while (csvFile.hasNextLine()) {
csvRows = csvFile.nextLine();
if (first) {
first = false;
continue;
}
System.out.println(csvRows);
String[] fields = csvRows.split(",");
productCode = (fields[0].trim());
price = Double.parseDouble(fields[1].trim());
description = fields[2].trim();
weight = Double.parseDouble(fields[3].trim());
rating = Integer.parseInt(fields[4].trim());
category = fields[5].trim();
ageRestriction = Boolean.parseBoolean(fields[6].trim());
catalogue.addAProduct(new Item(productCode, price, description, weight, rating, category, ageRestriction));
}
csvFile.close();
}
}
ok so for a CSV file like this:
"1.0.0.0","1.0.0.255","16777216","16777471","AU","Australia"
"1.0.1.0","1.0.3.255","16777472","16778239","CN","China"
"1.0.4.0","1.0.7.255","16778240","16779263","AU","Australia"
"1.0.8.0","1.0.15.255","16779264","16781311","CN","China"
"1.0.16.0","1.0.31.255","16781312","16785407","JP","Japan"
"1.0.32.0","1.0.63.255","16785408","16793599","CN","China"
"1.0.64.0","1.0.127.255","16793600","16809983","JP","Japan"
"1.0.128.0","1.0.255.255","16809984","16842751","TH","Thailand"
here is a sample of how to read using Java Native Libraries
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class CSVReader {
public static void main(String[] args) {
CSVReader obj = new CSVReader();
obj.run();
}
public void run() {
String csvFile = YOURFILEPATHHERE ;
BufferedReader br = null;
String line = "";
String cvsSplitBy = ",";
try {
br = new BufferedReader(new FileReader(csvFile));
while ((line = br.readLine()) != null) {
// use comma as separator
String[] country = line.split(cvsSplitBy);
System.out.println("Country [code= " + country[4]
+ " , name=" + country[5] + "]");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("Done");
}
}
does this help?
If you are just doing a single look-up and then exiting then just remember the String you are looking for. As you parse the lines compare to see if you have a match and if you do then return that line.
For repeated searches that would be very inefficient though. Assuming your data set is not too large for memory you would be better off parsing the file and putting it into a Map:
Map<String, Data> dataMap = new HashMap<>();
Parse the file, putting all the lines into the map
Then the lookup just becomes:
Data d = dataMap.get(lineKey);
If d is null then there is no matching line. If it not null then you have found your line.
You can create an array list of object. An object for each line in the CSV. Then search the array object with your search criteria.
User CSVReader framework to read the csv file. Sample code (not exactly what you want)
FileInputStream fis = new FileInputStream(file);
CSVReader reader = new CSVReader(new BufferedReader( new InputStreamReader(fis, "UTF-8" )));
ArrayList<String> row = new ArrayList<String>();
ArrayList<Entry> entries = new ArrayList<Entry>();
// a line = ID, Name, Price, Description
while (!reader.isEOF()) {
reader.readFields(row);
if( row.size() >= 4)
entries.add(new Entry(row.get(0), row.get(1), row.get(2), row.get(3)));
}
System.out.println("Size : "+entries);

Write CSV file column-by-column

I was searching for an answer for this but I didn't find it. Does anyone have a solution for this kind of problem. I have a set of text variables that I have to write into the .CSV file using Java. I am currently doing a project with JavaScript that calls for Java. This is a function that I have right now that does the job well and writes the text into .CSV line by line.
function writeFile(filename, data)
{
try
{
//write the data
out = new java.io.BufferedWriter(new java.io.FileWriter(filename, true));
out.newLine();
out.write(data);
out.close();
out=null;
}
catch(e) //catch and report any errors
{
alert(""+e);
}
}
But now I have to write parts of text one by one like the example bellow.
first0,second0,third0
first1,second1,third1
first2,second2,third2
.
.
.
first9,second9,third9
So the algorithm goes like this. The function writes first0 with comma then goes to the next line writes first1, goes to next line writes first2 and so one until first9. After that part is done the script goes to the beginning of the file and writes second0 behind the comma, goes to the next line and writes second1 behind the comma and so on. You get the idea.
So now I need java
You might want to consider using Super CSV to write the CSV file. As well as taking care of escaping embedded double-quotes and commas, it offers a range of writing implementations that write from arrays/Lists, Maps or even POJOs, which means you can easily try out your ideas.
If you wanted to keep it really simple, you can assemble your CSV file in a two-dimensional array. This allows to to assemble it column-first, and then write the whole thing to CSV when it's ready.
package example;
import java.io.FileWriter;
import java.io.IOException;
import org.supercsv.io.CsvListWriter;
import org.supercsv.io.ICsvListWriter;
import org.supercsv.prefs.CsvPreference;
public class ColumnFirst {
public static void main(String[] args) {
// you can assemble this 2D array however you want
final String[][] csvMatrix = new String[3][3];
csvMatrix[0][0] = "first0";
csvMatrix[0][1] = "second0";
csvMatrix[0][2] = "third0";
csvMatrix[1][0] = "first1";
csvMatrix[1][1] = "second1";
csvMatrix[1][2] = "third1";
csvMatrix[2][0] = "first2";
csvMatrix[2][1] = "second2";
csvMatrix[2][2] = "third2";
writeCsv(csvMatrix);
}
private static void writeCsv(String[][] csvMatrix) {
ICsvListWriter csvWriter = null;
try {
csvWriter = new CsvListWriter(new FileWriter("out.csv"),
CsvPreference.STANDARD_PREFERENCE);
for (int i = 0; i < csvMatrix.length; i++) {
csvWriter.write(csvMatrix[i]);
}
} catch (IOException e) {
e.printStackTrace(); // TODO handle exception properly
} finally {
try {
csvWriter.close();
} catch (IOException e) {
}
}
}
}
Output:
first0,second0,third0
first1,second1,third1
first2,second2,third2
Here is my solution to the problem. You don't need to keep the whole data in the buffer thanks to the low-level random access file mechanisms. You would still need to load your records one by one:
package file.csv;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.List;
public class CsvColumnWriter {
public static void main(String args[]) throws Exception{
CsvColumnWriter csvWriter = new CsvColumnWriter(new File("d:\\csv.txt"), new File("d:\\csv.work.txt"), 3);
csvWriter.writeNextCol(Arrays.asList(new String[]{"first0", "first1", "first2"}));
csvWriter.writeNextCol(Arrays.asList(new String[]{"second0", "second1", "second2"}));
csvWriter.writeNextCol(Arrays.asList(new String[]{"third0", "third1", "third2"}));
}
public void writeNextCol(List<String> colOfValues) throws IOException{
// we are going to create a new target file so we have to first
// create a duplicated version
copyFile(targetFile, workFile);
this.targetStream = new BufferedOutputStream(new FileOutputStream(targetFile));
int lineNo = 0;
for(String nextColValue: colOfValues){
String nextChunk = nextColValue + ",";
// before we add the next chunk to the current line,
// we must retrieve the line from the duplicated file based on its the ofset and length
int lineOfset = findLineOfset(lineNo);
workRndAccFile.seek(lineOfset);
int bytesToRead = lineInBytes[lineNo];
byte[] curLineBytes = new byte[bytesToRead];
workRndAccFile.read(curLineBytes);
// now, we write the previous version of the line fetched from the
// duplicated file plus the new chunk plus a 'new line' character
targetStream.write(curLineBytes);
targetStream.write(nextChunk.getBytes());
targetStream.write("\n".getBytes());
// update the length of the line
lineInBytes[lineNo] += nextChunk.getBytes().length;
lineNo++;
}
// Though I have not done it myself but obviously some code should be added here to care for the cases where
// less column values have been provided in this method than the total number of lines
targetStream.flush();
workFile.delete();
firstColWritten = true;
}
// finds the byte ofset of the given line in the duplicated file
private int findLineOfset(int lineNo) {
int ofset = 0;
for(int i = 0; i < lineNo; i++)
ofset += lineInBytes[lineNo] +
(firstColWritten? 1:0); // 1 byte is added for '\n' if at least one column has been written
return ofset;
}
// helper method for file copy operation
public static void copyFile( File from, File to ) throws IOException {
FileChannel in = new FileInputStream( from ).getChannel();
FileChannel out = new FileOutputStream( to ).getChannel();
out.transferFrom( in, 0, in.size() );
}
public CsvColumnWriter(File targetFile, File workFile, int lines) throws Exception{
this.targetFile = targetFile;
this.workFile = workFile;
workFile.createNewFile();
this.workRndAccFile = new RandomAccessFile(workFile, "rw");
lineInBytes = new int[lines];
for(int i = 0; i < lines; i++)
lineInBytes[i] = 0;
firstColWritten = false;
}
private File targetFile;
private File workFile;
private int[] lineInBytes;
private OutputStream targetStream;
private RandomAccessFile workRndAccFile;
private boolean firstColWritten;
}
I'm just going ahead and assume that you have some freedom how to fulfill this task. To my knowledge, you can't 'insert' text into a file. You can only do it by reading the file completely, change it in-memory, and then write back the result into the file.
So it would be better if you invert your data structure in-memory and then write it. If your data object is a matrix, just transpose it, so that it is in the format you want to write.
How about this
Scanner input = new Scanner(System.in);
String[] lines = new String[9];
for (int j = 0; j < 2; j++) {
for (int i = 0; i < 9; i++) {
lines[i] += (String) input.nextLine() + ",";
}
}
for (int i = 0; i < 9; i++) {
lines[i] += (String) input.nextLine();
}
Based on your requirements of not losing any data if an error occurs, perhaps you should rethink the design and use an embedded database (there is a discussion of the merits of various embedded databases at Embedded java databases). You would just need a single table in the database.
I suggest this because in your original question it sounds like you are trying to use a CSV file like a database where you can update the columns of any row in any order. In that case, why not just bite the bullet and use a real database.
Anyhow, once you have all the columns and rows of your table filled in, export the database to a CSV file in "text file order" row1-col1, row1-col2 ... row2-col1 etc.
If an error occurs during the building of the database, or the exporting of the CSV file at least you will still have all the data from the previous run and can try again.

Best way to process strings in Java

I want make a game of life clone using text files to specify my starting board, then write the finished board to a new text file in a similar format. E.g. load this:
Board in:
wbbbw
bbbbb
wwwww
wbwbw
bwbwb
from a file, then output something like this:
Board out:
wbbbw
bbbbb
wwwww
wwwww
bwbwb
I do this by making a 2D array (char[][] board) of characters, reading the file line by line into a string, and using String.charAt() to access each character and store it in the array.
Afterward, I convert each element of board (i.e., board[0], board[1], etc.), back to a string using String.valueOf(), and write that to a new line in the second file.
Please tell me I'm an idiot for doing this and that there is a better way to go through the file -> string -> array -> string -> file process.
You can use String.toCharArray() for each line while reading the file.
char[][] board = new char[5][];
int i = 0;
while((line = buffRdr.readLine()) != null) {
board[i++] = line.toCharArray();
}
And while writing either String.valueOf() or java.util.Arrays.toString().
for(int i=0; i<board.length; i++) {
//write Arrays.toString(board[i]);
}
// Remember to handle whitespace chars in array
char[] row = "wbbbw bbbbb wwwww wbwbw bwbwb".toCharArray()
Everything else seems good.
Why not use an already existing text format such as JSON instead of inventing your own?
There are tons of JSON parsers out there that can read and write two dimensional arrays.
You get both the benefit of easy reading directly from the file(as with your original method) and the benefit of not having to parse an annoying string format.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
public class GameOfLife
{
private String mFilename;
private ArrayList<String> mLines;
public GameOfLife(String filename)
{
mFilename = filename;
read();
}
public char get(int x, int y)
{
String line = mLines.get(y);
return line.charAt(x);
}
public void set(char c, int x, int y)
{
String line = mLines.get(y);
String replacement = line.substring(0, x) + c + line.substring(x + 1, line.length());
mLines.set(y, replacement);
}
private void read()
{
mLines = new ArrayList<String>();
try
{
BufferedReader in = new BufferedReader(new FileReader(mFilename));
String line = in.readLine();
while (line != null)
{
mLines.add(line);
line = in.readLine();
}
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}
private void write()
{
try
{
BufferedWriter out = new BufferedWriter(new FileWriter(mFilename));
for (String line : mLines)
{
out.write(line + "\n");
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}

Categories

Resources