使用 Apache 的 commons-csv 实现 CSV 文件导出

1. CSV 简介

CSV全称是:Comma Separated Values (逗号分隔值)或者 Character Separated Values(字符分隔值)。其文件以纯文本形式存储表格数据(数字和文本)。CSV文件由任意数目的记录组成,记录间以某种换行符分隔;每条记录由字段组成,字段间的分隔符是其它字符或字符串,最常见的是逗号或制表符。每一行记录位于一个单独的行上,用回车换行符CRLF(也就是\r\n)分割。

对于excel来说默认使用 ,进行分割数据。

每一行记录最后一个字段后不能跟逗号

每一行一条记录

列为空需要指定 “”

用回车换行符CRLF(\r\n)分割每条记录

纯文本,使用某个字符集,比如ASCII、Unicode、EBCDIC或GB2312

2. 实现 CSV 文件导出

采用 Apache 开源的 commons-csv 包,详细内容参考 官网

<dependency>  
    <groupId>org.apache.commons</groupId>  
    <artifactId>commons-csv</artifactId>  
    <version>1.7</version>  
</dependency>

2.1.2 API
print(Object value); // 写入一个单元格数据
printRecord(Iterable<?> values); // 写入一行数据 printRecords(Iterable<?> values);// 写入多行数据
在这里插入图片描述
2.2 封装一个导出方法
/**

  • 导出 csv 文件
  • [@param] out 输出流
  • [@param] iter 数据 我这里传 List<List> 类型
  • [@param] charset 字符集编码
  • [@param] header 表头
    */
public void exportCSVFile(OutputStream out, Iterable<?> iter, String charset, String... header) {  
	  try {  
		  // 写入bom, 防止中文乱码  
		  byte[] bytes = {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};  
		  out.write(bytes);  

		  OutputStreamWriter osw = new OutputStreamWriter(out, charset);  
		  CSVFormat csvFormat = CSVFormat.EXCEL.withHeader(header);  

		  CSVPrinter csvPrinter = new CSVPrinter(osw, csvFormat);  
		  csvPrinter.printRecords(iter);  
		  csvPrinter.flush();  
		  csvPrinter.close();  
	  } catch (IOException e) {  
		  e.printStackTrace();  
	  }  
}

2.3 使用示例

List<List<Object>> lists = new ArrayList<>();List<Object> list1 = new ArrayList<>();  
list1.add("张三");  
list1.add(18);  
list1.add("男");  
lists.add(list1);List<Object> list2 = new ArrayList<>();  
list2.add("李四");  
list2.add(20);  
list2.add("女");  
lists.add(list2);  

String[] header = {"姓名", "年龄", "性别"};  

// 省略 out  

exportCSVFile(out, lists, "UTF-8", header);

在这里插入图片描述
文本编辑器打开

张三,18,男

李四,20,女
2.4 可能遇到的问题
使用 Excel 打开出现中文乱码
上面的示例代码采用的是写入 bom ,编码为 UTF-8 的方式解决的,也有其他方案是使用编码为 GBK。
用 Excel 打开后,日期格式会变成 ### 或者 2019/01/01 以及较大数字会变成科学技术法
写数据时加上制表符,一个制表符不够,就 2 个
不要调用 printRecords(); 方法,而是通过遍历数据调用 csvPrinter.print(“\t”+ 数据 +“\t”);

public void exportCSVFile(OutputStream out, Iterable<?> iter, String charset,String... header) {  
	  try {  
		  // 写入bom, 防止中文乱码  
		  byte[] bytes = {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};  
		  out.write(bytes);  

		  OutputStreamWriter osw = new OutputStreamWriter(out, charset);  
		  CSVFormat csvFormat = CSVFormat.EXCEL.withHeader(header);  

		  CSVPrinter csvPrinter = new CSVPrinter(osw, csvFormat);  
		  Iterator<?> iterator = iter.iterator();  
		  while (iterator.hasNext()) {  
			  Collection list = (Collection)iterator.next();  
			  // 开始写一行数据  
			  list.forEach(c->{  
				  try {  
					  csvPrinter.print("\t" +c.toString() +"\t" );  
				  } catch (IOException e) {  
					  e.printStackTrace();  
				  }  
			  });  
			  // 写完一行,需要换行  
			  csvPrinter.println();  
		  }  
		  csvPrinter.flush();  
		  csvPrinter.close();  
	  } catch (IOException e) {  
		  e.printStackTrace();  
	  }  
}

在这里插入图片描述
写入的数据包含逗号,双引号等特殊字符时,导出的 csv 文件用文本编辑器打开,数据会用双引号包裹起来, 如下所示:
在这里插入图片描述
如果CSV文件不包含标题,或者不确定是否包含标题,则可以使用索引访问记录。由于CSVRecord实现了Java Iterable Interface,因此即使使用Excel和大多数其他应用程序打开CSV索引,其索引也从1开始,但索引是基于0的:

CSVParser csvParser = CSVFormat.DEFAULT.parse(new InputStreamReader(csvFile.getInputStream()));
for (CSVRecord record : csvParser) {
    String field_1 = record.get(0);
    String field_2 = record.get(1);
    ...
}

让我们考虑一个带有树数据的示例CSV文件,并将其称为“树数据CSV”,以供本文将来参考:
指数 周长(英寸) 高度(英尺) 体积(英尺)
1个 8.3 70 10.3
要引用数据的每一行,我们可以像上一个示例一样使用索引或列标题:

InputStreamReader input = new InputStreamReader(csvFile.getInputStream());
CSVParser csvParser = CSVFormat.EXCEL.withFirstRecordAsHeader().parse(input);
for (CSVRecord record : csvParser) {
    String field_1 = record.get("Index");
    String field_2 = record.get("Girth (in)");
    String field_3 = record.get("Height (ft)");
    String field_4 = record.get("Volume (ft)");
}

如果要读取不包含标题行的文件,想要定义自己的标题或使索引混乱,则Apache Commons还允许定义标题以进行解析。

.withFirstRecordAsHeader()您可以手动定义标题,而不是在定义CSV文件格式时使用该方法。例如,如果要避免在树数据文件的标题中引用度量单位,则可以重新定义标题以使用自己的字符串值:

CSVParser csvParser = CSVFormat.REF4180.withHeader("Index", "Girth", "Height", "Volume");
for (CSVRecord record : csvParser) {
    String field_2 = record.get("Girth");
}

如果您的CSV文件包含标题,但是您想定义自己的标题并跳过读取文件中的标题,请使用.readNext()以跳过第一行:

CSVRecord header = csvParser.readNext();
// read the other rows in a loop as usual
public enum treeHeader {
    Index, Girth, Height, Volume
}
...
CSVParser csvParser = CSVFormat.DEFAULT.withHeader(treeHeader.class).parse(input);
// read rows

字节流直接操作文件本身,字符流则是针对内存开辟一个缓冲区
什么是缓冲区?为什么要做缓冲区?

缓冲区通俗来说就是一个暂时存储数据的容器,比如你频繁操作一个文件或者查询数据库数据,肯定影响性能,但是如果你开辟一个缓冲区,将之前查到的数据存在内存中,下次直接在内存读取,速度会快很多。缓冲区就跟这个概念差不多,将读取到的数据先全部放在缓冲区内,等到flush()或者close() 在统一输出到文件中去。

InputStream实现一个字节一个字节的读取,而InputStreamReader是升级版实现一个字符一个字符的读取,BufferedReader再次升级实现一行一行的读取;

/**
 * 创建字符输出流并且写完后不关闭流
 */
public class demo {
    public static void main(String[] args) throws Exception{
        //字符流写数据 并且不关闭字符流
         OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File("D:\\tjbh项目\\1111.txt")));
         writer.write("12345");
    }
}

在这里插入图片描述
可以看到并没有在文件中显示内容,说明现在数据还都在缓冲区内,这时候只要close()或者 flush() 就可以将数据存到文件中

/**
 * 关闭流
 */
public class demo {
    public static void main(String[] args) throws Exception{
        //字符流写数据 并且不关闭字符流
         OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File("D:\\tjbh项目\\1111.txt")));
         writer.write("12345");
         writer.close();
    }

现一行一行的读取,那么就用到了BufferedReader

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Apache Commons CSV是一个Java库,可以用来读取、写入和处理CSV文件。如果你想使用它来导出字符串格式的数据,可以按照以下步骤进行操作: 1. 引入依赖 在你的项目中引入Apache Commons CSV的依赖。 Maven用户可以在pom.xml中添加以下代码块: ``` <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-csv</artifactId> <version>1.8</version> </dependency> ``` Gradle用户可以在build.gradle中添加以下代码: ``` implementation 'org.apache.commons:commons-csv:1.8' ``` 2. 创建CSVPrinter对象 创建一个CSVPrinter对象来写入CSV数据到字符串中。CSVPrinter的构造函数需要一个Writer对象作为参数,可以使用StringWriter来创建一个Writer对象。 ``` StringWriter writer = new StringWriter(); CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT); ``` 3. 写入数据 使用CSVPrinter的printRecord方法将数据写入CSV文件中。printRecord方法接受一个可变参数列表,可以传递任意数量的值。 ``` csvPrinter.printRecord("Name", "Age", "Gender"); csvPrinter.printRecord("John", 25, "Male"); csvPrinter.printRecord("Jane", 30, "Female"); ``` 4. 获取字符串格式的数据 调用StringWriter的toString方法可以获取CSV数据的字符串格式。 ``` String csvData = writer.toString(); ``` 完整的代码示例: ``` StringWriter writer = new StringWriter(); CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT); csvPrinter.printRecord("Name", "Age", "Gender"); csvPrinter.printRecord("John", 25, "Male"); csvPrinter.printRecord("Jane", 30, "Female"); String csvData = writer.toString(); ``` csvData字符串的值为: ``` Name,Age,Gender John,25,Male Jane,30,Female ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值