【问题标题】:Appending to CSV file without headers附加到没有标题的 CSV 文件
【发布时间】:2018-02-22 08:25:14
【问题描述】:

我正在使用 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());
        }
    }
  }
}

【问题讨论】:

标签: java opencsv


【解决方案1】:

我不知道该功能是否最终被添加到 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(...)

【讨论】:

  • 您可以提出您有价值的实现作为对 opencsv 项目的贡献。追加到现有的 CSV 文件是 opencsv 项目中大量缺少的功能!
【解决方案2】:

不错。追加是我们在 opencsv 中编写时并没有考虑太多的事情,因为它具有潜在的风险(出现问题,您可能会破坏好文件),因此更倾向于写入。

也就是说,在 sourceforge 中打开一个错误或功能请求,如果有足够的兴趣,我们将尝试在 4.3 版本中获得它(4.2 已被预定)。

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

希望对您有所帮助。

:)

【讨论】:

  • generateHeader 方法的覆盖不起作用。作者将写一个空行。
  • 您好,抱歉,覆盖 generateHeader 会添加一个完整的空行。这样做的正确方法是什么?
  • 如果您使用的是 ColumnPositionMappingStrategy,您应该在模型类中的每个字段下使用 @CsvBindByPosition 注释指定位置
  • 考虑在 opencsv 项目中添加 @afarrapeira 的代码(参见 stackoverflow.com/a/60222383/363573)怎么样。这将是一项宝贵的贡献。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-21
  • 2021-10-06
  • 2016-03-25
  • 2021-02-19
  • 2021-07-15
  • 2021-11-26
  • 2014-05-17
相关资源
最近更新 更多