osiegmar / FastCSV

CSV library for Java that is fast, RFC-compliant and dependency-free.
https://fastcsv.org/
MIT License
551 stars 93 forks source link

csvReader.read(file, StandardCharsets.UTF_8); Gives Null even if it's contains a header #38

Closed DanielMartensson closed 4 years ago

DanielMartensson commented 5 years ago

Hi!

I have a issue here. I know that you have write the code like this, but this line gives null

CsvContainer csvContainer = csvReader.read(file, StandardCharsets.UTF_8);

Even if I have a header. I understand if csvContainer is null if the CSV file is empty, but it should not be null if csvContainer has at least one row or a header.

Edit:

Found out that this can be done this way.

public void newRow(String rowText) {
        try {
            CsvParser csvParser = csvReader.parse(file, StandardCharsets.UTF_8);
            Collection<String[]> data = new ArrayList<>();
            csvParser.nextRow(); // Need to call this to get the header
            data.add((String[]) csvParser.getHeader().toArray()); // Add header
            CsvRow csvRow;
            while((csvRow = csvParser.nextRow()) != null)
                data.add((String[]) csvRow.getFields().toArray()); // Add existing lines to data

            data.add(rowText.split(delimiter)); // Add the new line to data with a new line
            csvWriter.write(file, StandardCharsets.UTF_8, data); // Auto close
        } catch (IOException e) {
            dialogs.exception("Cannot add new rows", e);
        }
    }

FastCSV should have a method where to append text to files. I know that you have such method, but that will first remove all data, then fill.

It's a great library and I will use it with Deeplearning4j. But I wonder if you could make an interface so it would be easier to use?

Easier I mean by a programmer should not need to write this much code for null exceptions

package se.danielmartensson.tools;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import de.siegmar.fastcsv.reader.CsvContainer;
import de.siegmar.fastcsv.reader.CsvParser;
import de.siegmar.fastcsv.reader.CsvReader;
import de.siegmar.fastcsv.reader.CsvRow;
import de.siegmar.fastcsv.writer.CsvAppender;
import de.siegmar.fastcsv.writer.CsvWriter;
import javafx.scene.control.Alert.AlertType;

/**
 * The reason why we are using FastCSV and not SQLite, is due to memory use.
 * @author Daniel Mårtensson
 *
 */
public class CSVHandler {
    private Dialogs dialogs = new Dialogs();
    private CsvReader csvReader;
    private CsvWriter csvWriter;;
    private File file;
    private String delimiter;

    /**
     * Constructor 
     * @param fileHandler File handler object
     * @param filePath Path to our file
     * @param delimiter Separator "," or ";" etc.
     * @param headers String that contains name of columns with delimiter as separator
     */
    public CSVHandler(FileHandler fileHandler, String filePath, String delimiter, String headers) {
        file = fileHandler.loadFile(filePath);
        this.delimiter = delimiter;
        csvWriter = new CsvWriter();
        csvReader = new CsvReader();
        csvReader.setFieldSeparator(delimiter.charAt(0));
        csvWriter.setFieldSeparator(delimiter.charAt(0));

        /*
         * Check if file has 0 rows = empty
         */
        if(getTotalRows() == 0)
            newHeader(headers); // Write our header if we don't have one
        csvReader.setContainsHeader(true);

    }

    /**
     * Get a single cell
     * @param row Row index
     * @param header Header name
     * @return String
     */
    public String getCell(int row, String header) {
        try {
            CsvContainer csvContainer = csvReader.read(file, StandardCharsets.UTF_8);
            int totalRows = csvContainer.getRowCount();
            if(row > totalRows)
                dialogs.alertDialog(AlertType.WARNING, "Index", "Index out of bounds: " + row + " > " + totalRows);
            else
                for (int i = 0; i < totalRows; i++)
                    if(i == row)
                        return csvContainer.getRow(i).getField(header); // Success!
            return ""; // Nothing happens!
        } catch (IOException | NullPointerException e) {
            dialogs.exception("Cannot get cell. Returning empty string", e);
            return ""; // Empty
        }
    }

    /**
     * Return a complete row
     * @param row Row number that we want to return
     * @return
     */
    public List<String> getRow(int row) {
        try {
            CsvContainer csvContainer = csvReader.read(file, StandardCharsets.UTF_8);
            return csvContainer.getRow(row).getFields();
        }catch(IOException | NullPointerException e) {
            dialogs.exception("Cannot get rows. Return List<String> = null", e);
            return null;
        }
    }

    /**
     * Set one value to a single cell
     * @param row Row number
     * @param header Our string header
     * @param cellValue Our value that we want to insert
     */
    public void setCell(int row, String header, String cellValue) {
        try {
            /*
             * Get total columns and get the current cell value in a row 
             */
            CsvContainer csvContainer = csvReader.read(file, StandardCharsets.UTF_8);
            CsvRow csvRow = csvContainer.getRow(row);
            String currentCell = csvRow.getField(header);
            int totalColumns = csvContainer.getRow(row).getFields().size();

            /*
             * Search for column index by searching for a know cell value
             */
            int columIndex = 0;
            while(columIndex < totalColumns)
                if(csvRow.getField(columIndex).equals(currentCell))
                    break;
                else
                    columIndex++;

            /*
             * Insert cellValue in column and insert row in container
             */
            csvRow.getFields().set(columIndex, cellValue); 
            csvContainer.getRows().set(row, csvRow); // TODO: Testa ta bort denna rad

            /*
             * Collect and write all
             */
            writeAll(csvContainer);
        } catch (IOException | NullPointerException e) {
            dialogs.exception("Cannot set cell", e);
        }
    }

    /**
     * Replace a whole row
     * @param row row number
     * @param text text with delimiter separator
     */
    public void replaceRow(int row, String text) {
        try {
            /*
             * Replace all items in a row
             */
            CsvContainer csvContainer = csvReader.read(file, StandardCharsets.UTF_8);
            CsvRow csvRow = csvContainer.getRow(row);
            String[] list = text.split(String.valueOf(delimiter));
            int totalColumns = csvContainer.getRow(row).getFields().size();
            if(list.length == totalColumns) {
                for(int i = 0; i < list.length; i++)
                    csvRow.getFields().set(i, list[i]);

                /*
                 * Insert row in container
                 */
                csvContainer.getRows().set(row, csvRow); // TODO: Testa ta bort denna rad

                /*
                 * Collect and write all
                 */
                writeAll(csvContainer);
            }else {
                dialogs.alertDialog(AlertType.ERROR, "Insert", "Not same dimension as CSV file");
            }
        } catch (IOException | NullPointerException e) {
            dialogs.exception("Cannot replace row", e);
        }
    }

    /**
     * Search for a cell value in a g
     * @param cellValue The cell in form of a string
     * @param header Name of the column
     * @return boolean
     */
    public boolean exist(String cellValue, String header) {
        try {
            CsvContainer csvContainer = csvReader.read(file, StandardCharsets.UTF_8);
            if(csvContainer == null)
                return false; // Nothing has been added, except the header
            for(int i = 0; i < csvContainer.getRowCount(); i++)
                if(cellValue.equals(csvContainer.getRow(i).getField(header)) == true)
                    return true; // Yes
            return false; // Nope
        } catch (IOException | NullPointerException e) {
            dialogs.exception("Cannot check existens. Returning false", e);
            return false;
        }
    }

    /**
     * Find on which row cellValue is on a header
     * @param cellValue
     * @param header
     * @return int
     */
    public int findRow(String cellValue, String header) {
        try {
            CsvContainer csvContainer = csvReader.read(file, StandardCharsets.UTF_8);
            for(int i = 0; i < csvContainer.getRowCount(); i++)
                if(cellValue.equals(csvContainer.getRow(i).getField(header)) == true)
                    return i; // Yes
            return 0; // Nope
        } catch (IOException | NullPointerException e) {
            dialogs.exception("Cannot find row index. Returning 0", e);
            return 0;
        }
    }

    /**
     * Delete the whole row at least if we got a row
     * @param row row number 
     */
    public void deleteRow(int row)  {
        try {
            /*
             * Remove a selected row
             */
            CsvContainer csvContainer = csvReader.read(file, StandardCharsets.UTF_8);
            csvContainer.getRows().remove(row);
            writeAll(csvContainer);

        } catch (IOException | NullPointerException e) {
            dialogs.exception("Cannot delete row", e);
        }
    }

    /**
     * Write all to the file
     * @param csvContainer The CsvContainer object
     * @throws IOException
     */
    private void writeAll(CsvContainer csvContainer) throws IOException {
        /*
         * Collect and write all
         */
        Collection<String[]> data = new ArrayList<>();
        for(CsvRow csvRow : csvContainer.getRows())
            data.add((String[]) csvRow.getFields().toArray());
        csvWriter.write(file, StandardCharsets.UTF_8, data); // Auto close
    }

    /**
     * Write a new header to the CSV file - This won't give us csvAppender == null if we have empty file
     * @param rowText Enter the string
     */
    public void newHeader(String rowText) {
        try {
            Collection<String[]> data = new ArrayList<>();
            data.add(rowText.split(delimiter)); // Add the header data
            csvWriter.write(file, StandardCharsets.UTF_8, data); // Auto close
        } catch (IOException | NullPointerException e) {
            dialogs.exception("Cannot write now row", e);
        }
    }

    /**
     * Create a new row
     * @param rowText
     */
    public void newRow(String rowText) {
        try {
            CsvParser csvParser = csvReader.parse(file, StandardCharsets.UTF_8);
            Collection<String[]> data = new ArrayList<>();
            CsvRow csvRow = csvParser.nextRow(); // Need to call this to get the header
            data.add((String[]) csvParser.getHeader().toArray()); // Add header
            data.add((String[]) csvRow.getFields().toArray()); // Add the row under the header
            while((csvRow = csvParser.nextRow()) != null) {
                data.add((String[]) csvRow.getFields().toArray()); // Add existing lines to data
            }

            data.add(rowText.split(delimiter)); // Add the new line to data with a new line
            csvWriter.write(file, StandardCharsets.UTF_8, data); // Auto close
        } catch (IOException e) {
            dialogs.exception("Cannot add new rows", e);
        }
    }

    /**
     * Return total rows
     * @return int total rows
     */
    public int getTotalRows() {
        try {
            CsvContainer csvContainer = csvReader.read(file, StandardCharsets.UTF_8);
            if(csvContainer == null)
                return 0; // Null means no rows here
            return csvContainer.getRowCount();
        } catch (IOException e) {
            dialogs.exception("Cannot find total rows. Returning 0", e);
            return 0;
        }
    }

    /**
     * Return total columns, in this case, it's on row index 0
     * @return int total columns
     */
    public int getTotalColumns() {
        try {
            CsvContainer csvContainer = csvReader.read(file, StandardCharsets.UTF_8);
            if(csvContainer == null)
                return 0; // Null means no rows here
            return csvContainer.getRow(0).getFields().size();
        } catch (IOException e) {
            dialogs.exception("Cannot find total columns. Returning 0.", e);
            return 0;
        }
    }
}
bhemar commented 4 years ago

Hi,

I also have this problem. I have an empty CSV file and I get Nullpointer exception, which is not good.

Even javadoc on "read" method says that null is never returned, but it is.

Please fix it so null is never returned, or at least update javadoc.

osiegmar commented 4 years ago

Thanks for pointing that out! The bug (null CsvContainer) is fixed with 474de29. Please let me know if there are doubts! Your other issue (append to an existing file) is tracked in #24.