附加到没有标题的CSV文件

时间:2018-02-22 08:25:14

标签: java opencsv

我正在使用opencsv将Java bean写入带有标头的CSV文件。文件名包含当前日期。如果用户在同一天再次运行它,它会附加到该文件,但会添加另一个标题行。

如何附加到文件但没有列标题。

    public class CSVExport {

final File USERHOME = new File(System.getProperty("user.home"));
final List<saleExport> listSaleExport = new ArrayList<>();
final ObjectMapper mapper = new ObjectMapper();

public void createCsvFile(String Region, Map<String, String> currentSale) throws IOException {

    mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
    // use column position mapping strategy for no headers?
    LocalDate today = LocalDate.now();

                final SaleExport saleExport = mapper.convertValue(currentSale, SaleExport.class);
                listSaleExport.add(saleExport);
                writeToFile(today +" LA.csv", listSaleExport);
}

public void writeToFile(String filename, List<listSaleExport> listSaleExport) throws IOException {

    File file = new File(PROCESSED_DIR +"\\", "filename");

    if (!file.exists()) {
        try {
            Writer writer = new FileWriter(PROCESSED_DIR +"\\" +filename, true);
            StatefulBeanToCsvBuilder<listSaleExport> beanToCsv = new StatefulBeanToCsvBuilder<>(writer);
            StatefulBeanToCsv<listSaleExport> beanWriter = beanToCsv.build();
            beanWriter.write(listSaleExport);
            writer.close();
        } catch (Exception ex) {
            System.out.println("Error : " + ex.getMessage());
        }
    } else {
        try {
            Writer writer = new FileWriter(PROCESSED_DIR +"\\" +"filename");
            StatefulBeanToCsvBuilder<listSaleExport> beanToCsv = new StatefulBeanToCsvBuilder<>(writer);
            StatefulBeanToCsv<listSaleExport> beanWriter = beanToCsv.build();
            beanWriter.write(listSaleExport);
            writer.close();
        } catch (Exception ex) {
            System.out.println("Error : " + ex.getMessage());
        }
    }
  }
}

2 个答案:

答案 0 :(得分:3)

好的。当我们在opencsv中写作时,追加是我们没有想到的,因为它有潜在的风险(出了问题,你可能会破坏什么是好文件)所以写作更受青睐。

那说在sourceforge中打开一个bug或功能请求,如果有足够的兴趣,我们将尝试在4.3版本中获取它(4.2已经预订固定)。

如果你想解决这个问题,那就创建你自己的扩展MappingStrategyHeaderColumnNameMappingStrategy类,并且你需要的只是在generateHeader方法上覆盖一个空字符串数组。您可以查看ColumnPositionMappingStrategy中的代码,看看我在说什么。这样可以防止写入标题。在这种情况下,您只需要在else部分使用它。

希望有所帮助。

:)

答案 1 :(得分:0)

我不知道该功能是否最终已添加到OpenCSV中。现在最新版本是5.1,但我还没有阅读任何内容...

正如我在another question中发布的那样,我将其更新为5.1后就可以使用:

需要同时具有特定字段标题(@CsvBindByName)和位置(@CsvBindByPosition)的CSV,所以我已经必须制定自己的MappingStrategy。

public class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
    private boolean useHeader=true;
    
    public CustomMappingStrategy(){
    }
    
    public CustomMappingStrategy(boolean useHeader) {
        this.useHeader = useHeader;
    }
    
    @Override
    public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
        final int numColumns = FieldUtils.getAllFields(bean.getClass()).length;
        super.setColumnMapping(new String[numColumns]);
        
        if (numColumns == -1) {
            return super.generateHeader(bean);
        }
        
        String[] header = new String[numColumns];
        
        if(!useHeader){
            return ArrayUtils.EMPTY_STRING_ARRAY;
        }
        BeanField<T, Integer> beanField;
        for (int i = 0; i < numColumns; i++){
            beanField = findField(i);
            String columnHeaderName = extractHeaderName(beanField);
            header[i] = columnHeaderName;
        }
        
        return header;
    }
    
    private String extractHeaderName(final BeanField<T, Integer> beanField){
        if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0){
            return StringUtils.EMPTY;
        }
        
        //return value of CsvBindByName annotation
        final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0];
        return bindByNameAnnotation.column();
    }

}

我还制作了一个通用的导出器类,以简化CSV生成:

public class ExportGenericCSV<T> {
    private static final Logger logger = LoggerFactory.getLogger(ExportGenericCSV.class);
    private final char DEFAULT_QUOTE_CHARACTER = CSVWriter.NO_QUOTE_CHARACTER;
    private final char CSV_SEPARATOR = CSVWriter.DEFAULT_SEPARATOR;
    
    public void writeToFile(String fileName, List<T> content){
        writeList(fileName, content, true);
    }
    
    public void appendToFile(String fileName, List<T> content){
        writeList(fileName, content, false, StandardOpenOption.APPEND);
    }

    @SuppressWarnings("unchecked")
    private void writeList(String fileName, List<T> exports, boolean addHeader, OpenOption...openOptions){
        if(exports == null  || exports.isEmpty()) return;
        try (Writer writer = Files.newBufferedWriter(Paths.get(fileName), openOptions)){

            CustomMappingStrategy<T> mapping = new CustomMappingStrategy<T>(addHeader);
            mapping.setType((Class<? extends T>) exports.get(0).getClass());
            
            StatefulBeanToCsv<T> beanToCsv = new StatefulBeanToCsvBuilder<T>(writer)
                    .withQuotechar(DEFAULT_QUOTE_CHARACTER)
                    .withSeparator(CSV_SEPARATOR)
                    .withMappingStrategy(mapping)
                    .build();

            beanToCsv.write(exports);
            
            writer.flush();
            writer.close();
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        } catch (CsvDataTypeMismatchException e) {
            logger.error(e.getMessage(), e);
        } catch (CsvRequiredFieldEmptyException e) {
            logger.error(e.getMessage(), e);
        }
    }
}

这样,当您需要导出项目列表时,可以使用:

ExportGenericCSV<ExportedClass> exporter = new ExportGenericCSV<ExportedClass>();

然后使用exporter.writeToFile(...)exporter.appendToFile(...)