How to skip blank cells when reading a csv file in Java? - java

Trying to create a csv file reader, but the reader gets an ArrayIndexOutOfBoundsException when it comes across an end of line blank.
Example: Name, Email, Age, City
John, johnsmith#email, 23, New York - This works and is standard
John, johnsmith#email, 23, - This fails
,,23,New York - This works
John, johnsmith#email,, - This fails
Any additional feedback is welcome as well!
Here's the code.
public class Main {
static String filepath = "filepath.csv";
public static void main(String[] args) {
SQLiteDB db = new SQLiteDB();
CSVReader(filepath);
}
public static void CSVReader(String filepath) {
List<User> users = new ArrayList<User>();
BufferedReader br = null;
String line;
int count = 1;
try {
br = new BufferedReader(new FileReader(filepath));
while ((line = br.readLine()) != null) {
if(line.trim().length() > 0) {
User user = new User();
String[] userinfo = line.split(",(?=([^\"]|\"[^\"]*\")*$)");
user.firstName = userinfo[0];
user.lastName = userinfo[1];
user.email = userinfo[2];
user.gender = userinfo[3];
user.image = userinfo[4];
user.bank = userinfo[5];
user.transaction = userinfo[6];
user.bool1 = userinfo[7];
user.bool2 = userinfo[8];
user.city = userinfo[9];
users.add(user);
System.out.println("Count:" + count + " " + user.getBool2());
count++;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
}
}

For the city value, just use the ternary operator to check the array length before assignment:
user.city = userinfo.length == 10 ? userinfo[9] : "";

If this is not a homework project, why don't you use a library? There are several on GitHub, and here is one that seems easy to use - https://github.com/osiegmar/FastCSV.

Related

Returning Strings from a file between 2 specified strings in java

I've been searching the web and I can't seem to find a working solution.
I have a file containing theses lines:
Room 1
Coffee
Iron
Microwave
Room_end
Room 2
Coffee
Iron
Room_end
I want to print all Strings between Room 1 and Room_end. I want my code to start when it find Room 1, print line after Room 1 and stop when it get to the first Room_end it find.
private static String LoadRoom(String fileName) {
List<String> result = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
result = reader.lines()
.dropWhile(line -> !line.equals("Room 1"))
.skip(1)
.takeWhile(line -> !line.equals("Room_end"))
.collect(Collectors.toList());
} catch (IOException ie) {
System.out.println("Unable to create " + fileName + ": " + ie.getMessage());
ie.printStackTrace();
}
for (int i = 0; i < result.size(); i++) {
System.out.println(result.get(i).getname());//error on getname because it cant work with Strings
}
}
class Model {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I am able to get a method to print all Strings of the file but not specific range of Strings. I also tried to work with Stream. My code feel quite messy, but I've been working on it for a while an it seems it only get messier.
I think there is a problem if you want to use lambda expression here:
lambda expressions are functional programming, and functional programming requires immutability, that means there should not be state related issue, you can call the function and give it same parameters and the result always will be the same, but in your case, there should be a state indicating whether you should print the line or not.
can you try this solution? I write it in python, but mainly it is just about a variable should_print that located outside of the scope
should_print = False
result = reader.lines()
for line in result:
if line == "Room end":
break
if should_print:
print(line)
if line == "Room 1":
should_print = True
keep a boolean value outside of the iteration, and check/update the value in each iteration
public static Map<String, List<String>> getRooms(String path) throws IOException {
Map<String, List<String>> result = new HashMap<>();
try (Scanner sc = new Scanner(new File(path))) {
sc.useDelimiter("(?=Room \\d+)|Room_end");
while (sc.hasNext()) {
Scanner lines = new Scanner(sc.next());
String room = lines.nextLine().trim();
List<String> roomFeatures = new ArrayList<>();
while (lines.hasNextLine()) {
roomFeatures.add(lines.nextLine());
}
if (room.length() > 0) {
result.put(room, roomFeatures);
}
}
}
return result;
}
is one way of doing it for your 'rooms file' though it should really be made more OO by making a Room bean to hold the data. Output with your file: {Room 2=[Coffee, Iron ], Room 1=[Coffee, Iron, Microwave]}
Switched my code and used this:
private static String loadRoom(String fileName) {
BufferedReader reader = null;
StringBuilder stringBuilder = new StringBuilder();
try {
reader = new BufferedReader(new FileReader(fileName));
String line = null; //we start with empty info
String ls = System.getProperty("line.separator"); //make a new line
while ((line = reader.readLine()) != null) { //consider if the line is empty or not
if (line.equals("Room 1")) { //condition start on the first line being "Room 1"
line = reader.readLine(); // read the next line, "Room 1" not added to stringBuilder
while (!line.equals("Room_end")) { //check if line String is "Room_end"
stringBuilder.append(line);//add line to stringBuilder
stringBuilder.append(ls);//Change Line in stringBuilder
line = reader.readLine();// read next line
}
}
}
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null)
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return stringBuilder.toString();
}
Here's a solution that uses a scanner and a flag. You may choose to break the loop when it reads "Room_end"
import java.util.Scanner;
import java.io.*;
public class Main{
private static String loadRoom(String fileName) throws IOException {
Scanner s = new Scanner(new File(fileName));
StringBuilder sb = new StringBuilder();
boolean print = false;
while(s.hasNextLine()){
String line = s.nextLine();
if (line.equals("Room 1")) print = true;
else if (line.equals("Room_end")) print = false;
else if (print) sb.append(line).append("\n");
}
return sb.toString();
}
public static void main(String[] args) {
try {
String content = loadRoom("content.txt");
System.out.println(content);
}catch(IOException e){
System.out.println(e.getMessage());
}
}
}

Java - Trying to output to a file where if the user's login is there only replace the balance

My program is basically a user's name, and balance, this is stored in a file, when the user updates their profile, I want their balance to update too, but not their name, as they already have it in the file.
The name and balance are split with a comma. In the file it is displayed like this:
Stacey,0.02
(The name is actually a randomly generated number+letter string, but I thought I'd keep it simple here.)
When I try to write to the file with this code, it doesn't write anything.
Code:
btnSaveUserid.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String text = txtUserid.getText().toString();
String balance = beedcoin1Balance.getText().toString();
File file = new File("d:/users/joel/desktop/code/usersid.txt");
BufferedReader br = null;
try {
String sCurrentLine;
br = new BufferedReader(new FileReader("d:/users/joel/desktop/code/usersid.txt"));
List<String> terms = new ArrayList<String>();
List<String> ttlBalance = new ArrayList<String>();
while ((sCurrentLine = br.readLine()) != null) {
String[] ar = sCurrentLine.split(",");
String userid = ar[0];
terms.add(userid);
System.out.println(terms);
}
if(terms.contains(text)) {
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("d:/users/joel/desktop/code/usersid.txt")));
Scanner scanner = new Scanner(file);
while(scanner.hasNextLine());
String line = scanner.nextLine();
String[] ar = line.split(",");
String userid = ar[0];
String bdcv1val = ar[1];
int lineNum = 0;
lineNum++;
if(line.equals(userid)) {
Path path = Paths.get("d:/users/joel/desktop/code/usersid.txt");
Charset charset = StandardCharsets.UTF_8;
String content = new String(Files.readAllBytes(path), charset);
content = content.replace(txtUserid.toString(), txtUserid.toString());
Files.write(path, content.getBytes(charset));
out.println(text + "," + balance);
System.out.println("Successfully printed to usersid.txt");
}
}
else {
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("d:/users/joel/desktop/code/usersid.txt")));
out.println(text + "," + balance);
System.out.println("Successfully printed to usersid.txt");
}
} catch (IOException el) {
el.printStackTrace();
} finally {
try {
if(br != null)br.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
It all takes place in a button's action listener, and this code outputs the "Successfully printed to file" but doesn't actually print it to the file. I'm honestly perplexed, any help would be greatly appreciated.

How to check if txt file contains a String, if so don't duplicate it

So right now I'm making a mod in Minecraft where it takes everyones username from a server and adds it to a txt file, it works but the the problem is I don't want to duplicate the names when I use the command again. Nothing has worked so far. How would I check if the txt already contains the username, don't add it again? Thank you. Again, I need it to before writing another name to the list, check the txt file if it already contains the name, if so don't add it.
for (int i = 0; i < minecraft.thePlayer.sendQueue.playerInfoList.size(); i++) {
List playerList = minecraft.thePlayer.sendQueue.playerInfoList;
GuiPlayerInfo playerInfo = (GuiPlayerInfo) playerList.get(i);
String playerName = StringUtils.stripControlCodes(playerInfo.name);
try {
fileWriter = new FileWriter(GameDirectory() + "\\scraped.txt", true);
bufferedReader = new BufferedReader(new FileReader(GameDirectory() + "\\scraped.txt"));
lineNumberReader = new LineNumberReader(new FileReader(GameDirectory() + "\\scraped.txt"));
} catch (IOException e) {
e.printStackTrace();
}
printWriter = new PrintWriter(fileWriter);
try {
fileWriter.write(playerName + "\r\n");
lineNumberReader.skip(Long.MAX_VALUE);
} catch (IOException e) {
e.printStackTrace();
}
printWriter.flush();
}
addMessage("Scraped " + lineNumberReader.getLineNumber() + " usernames!");
EDIT: Really need an answer guys :( Thank you
EDIT: this is what I have now, but it's not even writing it anymore.
List playerList = minecraft.thePlayer.sendQueue.playerInfoList;
for (int i = 0; i < minecraft.thePlayer.sendQueue.playerInfoList.size(); i++) {
GuiPlayerInfo playerInfo = (GuiPlayerInfo) playerList.get(i);
String playerName = StringUtils.stripControlCodes(playerInfo.name);
String lines;
try {
if ((lines = bufferedReader.readLine()) != null) {
if (!lines.contains(playerName)) {
bufferedWriter.write(playerName);
bufferedWriter.newLine();
bufferedWriter.flush();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
int linenumber = 0;
try {
while (lineNumberReader.readLine() != null) {
linenumber++;
}
} catch (IOException e) {
e.printStackTrace();
}
The logic of your second piece of code is wrong. If you write out the pseudo-code of it, it's easy to see why:
Open a File Reader at the start of the file
For every Player on the server
Save the player name
Read the next line of the file
If we have not reached the end of the file
If the player name is not on this line of the file
Write the name of the player to the file
You need to read the entire file outside of the loop, and then check if the player exists anywhere in the file, not just if it happens to be on the line which is the same position as the player on the server.
The easiest way to do this is to keep the players in a list while you're processing, and read/write them to file, like this:
public static List<String> loadPlayerList() throws FileNotFoundException
{
final Scanner scanner = new Scanner(new File(GameDirectory() + "\\scraped.txt"));
final List<String> players = new ArrayList<>();
while(scanner.hasNextLine())
players.add(scanner.nextLine());
return players;
}
public static void writePlayersList(final List<String> players) throws IOException
{
try(final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream((GameDirectory() + "\\scraped.txt")))))
{
for(final String player : players)
{
writer.write(player);
writer.newLine();
}
}
}
public static void main(String[] args) throws IOException
{
final List<String> players = loadPlayerList();
for(final GuiPlayerInfo player : minecraft.thePlayer.sendQueue.playerInfoList)
{
final String playerName = StringUtils.stripControlCodes(player.name);
if(!players.contains(playerName))
players.add(playerName);
}
writePlayersList(players);
}

Manipulating tokens to deal with variable number of strings on one line

I will try to explain this as much as I can. I am reading scores from a file onto which my form appends lines. The line consists of a date, home team, score, away team, score.
The stats I gather is away wins, home wins and draws.
The following code works perfectly
JButton viewStatsButton = new JButton(new AbstractAction("VIEW STATS")
{
public void actionPerformed( ActionEvent e )
{
int homeScore = 0;
int awayScore = 0;
int homeWins = 0;
int awayWins = 0;
int scoreDraw = 0;
String line = null;
String output;
String matchDay;
#SuppressWarnings("unused")
String homeTeam;
#SuppressWarnings("unused")
String awayTeam;
String file = "scores.dat";
StringTokenizer tokenizer;
FileReader fileReader = null;
try
{
fileReader = new FileReader (file);
}
catch (FileNotFoundException e1)
{
e1.printStackTrace();
}
BufferedReader inFile = new BufferedReader (fileReader);
try
{
line = inFile.readLine();
}
catch (IOException e1)
{
e1.printStackTrace();
}
while(line != null)
{
tokenizer = new StringTokenizer(line);
matchDay = tokenizer.nextToken();
homeTeam = tokenizer.nextToken();
try
{
homeScore = Integer.parseInt(tokenizer.nextToken());
}
catch (NumberFormatException exception)
{
System.out.println("Error in input. Line ignored:");
System.out.println(line);
}
awayTeam = tokenizer.nextToken();
try
{
awayScore = Integer.parseInt(tokenizer.nextToken());
}
catch (NumberFormatException exception)
{
System.out.println("Error in input. Line ignored:");
System.out.println(line);
}
if(homeScore > awayScore)
{
homeWins++;
}
else if(awayScore > homeScore)
{
awayWins++;
}
else
{
scoreDraw++;
}
try
{
line = inFile.readLine();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
try
{
inFile.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
output = "Home Wins : "+homeWins+"\nAway Wins : "+awayWins+"\nDraws : "+scoreDraw;
JTextArea textArea = new JTextArea();
frame.getContentPane().add(textArea, BorderLayout.CENTER);
textArea.setText(output);
}
});
scorePanel.add(viewStatsButton);
}
The problem does not come to light until the name of team is made out of two strings i.e.Newcastle United. What I had to do was append the two strings together like NewcastleUnited. I have tried to find out the length of the token and if it's less than 3 then i take it and parse it as integer but it seems that even if the next token reference is in an if statement it still moves to the token after it.
I would appreciate any help and guidance.
Try following
Before calling tokenizer.nextToken() check tokenizer.hasMoreTokens() to ensure that there is a token to read
if(tokenizer.hasMoreTokens())
{
x = tokenizer.nextToken();
}
After reading team name(first part) check whether next part is integer if it is, treat it as score, otherwise append it to team name.
homeTeam = tokenizer.nextToken();
String temp = tokenizer.nextToken();
try
{
homeScore = Integer.parseInt(temp);
}
catch(Exception e)
{
//Comes here if temp is not an integer, so temp is second part of name
homeTeam = homeTeam + " "+temp;
homeScore = Integer.parseInt(tokenizer.nextToken());
}
//Whatever the case, if we come here, it means both hometeam and score are assigned.
...........
...........
...........
Don't forgot to check tokenizer.hasMoreTokens() if you are not sure whether there is a token.

How can I read and write objects from an arraylist?

I´m creating an arraylist in my
UserArchive class and add User-objects from my User class:
public class UserArchive implements Serializable {
ArrayList<User> list = new ArrayList<User>();
// Inserts a new User-object
public void regCustomer(User u) {
list.add(u);
}
What is the best way to read and write this list?
I think this is the right way to write it?
public void writeFile() {
File fileName = new File("testList.txt");
try{
FileWriter fw = new FileWriter(fileName);
Writer output = new BufferedWriter(fw);
int sz = list.size();
for(int i = 0; i < sz; i++){
output.write(list.get(i).toString() +"\n");
}
output.close();
} catch(Exception e){
JOptionPane.showMessageDialog(null, "Kan ikke lage denne filen");
}
I´ve tried using BufferedReader to read the file, but can't get list.add(line) to work:
public void readFile() {
String fileName = "testList.txt";
String line;
try{
BufferedReader input = new BufferedReader(new FileReader(fileName));
if(!input.ready()){
throw new IOException();
}
while((line = input.readLine()) != null){
list.add(line);
}
input.close();
} catch(IOException e){
System.out.println(e);
}
}
I know the problem is that line is a String and should some how be a User. Is the problem that I cant use BufferedReader to do this? If so, how am I suppose to read the file?
The simplest way to do it is convert each user to csv format provided the user object is not complex.
For example your user class should look like this
public class User {
private static final String SPLIT_CHAR = ",";
private String field1;
private String field2;
private String field3;
public User(String csv) {
String[] split = csv.split(SPLIT_CHAR);
if (split.length > 0) {
field1 = split[0];
}
if (split.length > 1) {
field2 = split[1];
}
if (split.length > 2) {
field3 = split[2];
}
}
/**
* Setters and getters for fields
*
*
*/
public String toCSV() {
//check null here and pass empty strings
return field1 + SPLIT_CHAR + field2 + SPLIT_CHAR + field3;
}
}
}
While writing the object call
output.write(list.get(i).toCSV() +"\n");
and when reading you can call
list.add(new User(line));
Imagine a simple User class, like so:
public class User {
private int id;
private String username;
// Constructors, etc...
public String toString() {
StringBuilder sb = new StringBuilder("#USER $");
sb.append(id);
sb.append(" $ ");
sb.append(username);
return sb.toString();
}
}
For an user with id = 42 and username = "Dummy", the user String representation would be:
#USER $ 42 $ Dummy
At first glance, your code seems to successfully write these strings to a text file (I haven't tested it yet).
So, the problem lies in reading back the information. This, extracting meaningful information from (in this case) formatted text, is commonly known as parsing.
You want to parse this information, from the lines you read.
Adapting your code:
BufferedReader input = null;
try {
input = new BufferedReader(new FileReader(fileName));
String line;
while((line = input.readLine()) != null) {
list.add(User.parse(line));
}
} catch(IOException e) {
e.printStackTrace();
} finally {
if (input != null) { input.close(); }
}
Note the subtle difference. I've replaced list.add(line) with list.add(User.parse(line)). This is where the magic occurs. Let's go on and implement the parsing method.
public class User {
private int id;
private String username;
// ...
public static User parse(String line) throws Exception {
// Let's split the line on those $ symbols, possibly with spaces.
String[] info = line.split("[ ]*\\$[ ]*");
// Now, we must validate the info gathered.
if (info.length != 3 || !info[0].equals("#USER")) {
// Here would go some exception defined by you.
// Alternatively, handle the error in some other way.
throw new Exception("Unknown data format.");
}
// Let's retrieve the id.
int id;
try {
id = Integer.parseInt(info[1]);
} catch (NumberFormatException ex) {
throw new Exception("Invalid id.");
}
// The username is a String, so it's ok.
// Create new User and return it.
return new User(id, info[2]);
}
}
And you're done!

Categories