基於 java 註解的 csv 讀寫框架。java
super-csvgithub
簡單看了下,這兩個框架提供的特性都很是的基礎。apache
之前以爲 csv 文件的讀寫很是簡單,就懶得封裝。數組
最近一個月寫了兩次 csv 文件相關的東西,發現要處理的細節仍是有的,還浪費比較多的時間。bash
好比:框架
UTF-8 中文編碼使用 excel 打開亂碼,由於缺乏 BOM 頭。maven
不一樣類型字段轉化爲字符串,順序的指定,head 頭的指定,若是手寫都會很繁瑣。ide
讀取的時候最後 ,
後無元素,split 會缺失等。工具
爲了解決上述問題,此框架應運而生。
Fluent 流式寫法
基於 java 註解,支持自定義的轉換和靈活配置
內置 8 大基本類型以及 String 類型轉換
解決 Excel 直接打開,utf-8 亂碼問題
支持集合、數組、Map 的存取
支持對象中內嵌其餘對象
支持特殊字符轉義
jdk7+
maven 3.x
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>csv</artifactId>
<version>0.0.6</version>
</dependency>
複製代碼
演示基本類型的轉換
public class User {
private String name;
private int age;
private float score;
private double money;
private boolean sex;
private short level;
private long id;
private char status;
private byte coin;
//Getter & Setter & toString()
}
複製代碼
/** * 構建通用測試列表 * @return 列表 */
private List<User> buildCommonList() {
User user = new User();
short s = 4;
byte b = 1;
user.age(10)
.name("你好")
.id(1L)
.score(60)
.coin(b)
.level(s)
.money(200)
.sex(true)
.status('Y');
return Arrays.asList(user);
}
複製代碼
public void commonTest() {
final String path = "src\\test\\resources\\common.csv";
CsvWriteBs.newInstance(path)
.write(buildCommonList());
}
複製代碼
name,age,score,money,sex,level,id,status,coin
你好,10,60.0,200.0,true,4,1,Y,1
複製代碼
public void commonTest() {
final String path = "src\\test\\resources\\common.csv";
List<User> userList = CsvReadBs.newInstance(path)
.read(User.class);
System.out.println(userList);
}
複製代碼
[User{name='你好', age=10, score=60.0, money=200.0, sex=true, level=4, id=1, status=Y, coin=1}]
複製代碼
爲了靈活的配置和默認配置並存,使用工具類會大大下降靈活性。
爲了用戶使用的便利性,和後期拓展的靈活性。
CSV 有兩個引導類:
名稱 | 做用 |
---|---|
CsvWriteBs | csv 文件寫入引導類 |
CsvReadBs | csv 文件讀取引導類 |
方法 | 默認值 | 說明 |
---|---|---|
newInstance(final String path) | 必填 |
建立實例,而且指定待寫入文件路徑。 |
path (final String path) | 配置文件路徑,只有從新指定 path 路徑時須要調用。 | |
writeHead(boolean writeBom) | true |
是否寫入 head 頭,若是想指定名稱,能夠結合註解。只有無 head 信息時,會寫入。 |
writeBom(boolean writeBom) | true |
是否寫入 UTF8 BOM 頭,只有文件爲空時纔會寫入。 |
charset(String charset) | UTF-8 |
指定文件編碼 |
sort(ISort sort) | NoSort | 默認不進行字段排序 |
write(List list) | 無 |
待寫入的文件列表 |
escape | false | 是否進行特殊字符的轉換 |
方法 | 默認值 | 說明 |
---|---|---|
newInstance(final String path) | 必填 |
建立實例,而且指定待讀取文件路徑。 |
path (final String path) | 配置文件路徑,只有從新指定 path 路徑時須要調用。 | |
charset(String charset) | UTF-8 |
指定文件編碼 |
sort(ISort sort) | NoSort | 默認不進行字段排序 |
startIndex(int startIndex) | 1 | 文件的第二行,默認第一行是 head |
endIndex(int endIndex) | 文件的最後一行 | |
escape | false | 是否進行特殊字符的轉換 |
用於待處理對象的字段上。
/** * 字段顯示名稱 * 1. 默認使用 field.name * @return 顯示名稱 */
String label() default "";
/** * 讀取是否須要 * @return 是 */
boolean readRequire() default true;
/** * 寫入是否須要 * @return 是 */
boolean writeRequire() default true;
/** * 讀取轉換 * @return 處理實現類 */
Class<? extends IReadConverter> readConverter() default CommonReadConverter.class;
/** * 寫入轉換 * @return 處理實現類 */
Class<? extends IWriteConverter> writeConverter() default StringWriteConverter.class;
複製代碼
屬性 | 默認值 | 說明 |
---|---|---|
label | 字段名稱 | 用於 csv 頭生成 |
readRequire | true | 是否須要從 csv 文件讀取 |
writeRequire | true | 當前字段是否須要寫入 csv 文件 |
readConverter | CommonReadConverter | 將 csv 中的字符串轉化爲當前字段類型,支持 8 大基本類型+String |
writeConverter | StringWriteConverter | 直接調用當前字段值 toString() 方法,null 直接爲空字符串 |
其中 readConverter/writeConverter 支持用戶自定義
public class UserAnnotation {
@Csv(label = "名稱")
private String name;
@Csv(label = "密碼", readRequire = false, writeRequire = false)
private String password;
@Csv(label = "生日", readConverter = ReadDateConvert.class, writeConverter = WriteDateConvert.class)
private Date birthday;
//Getter & Setter & toString()
}
複製代碼
使咱們自定義的針對 Date 的轉換實現。
public class WriteDateConvert implements IWriteConverter<Date> {
@Override
public String convert(Date value) {
DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
return dateFormat.format(value);
}
}
複製代碼
public class ReadDateConvert implements IReadConverter<Date> {
@Override
public Date convert(String value, Class fieldType) {
try {
DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
return dateFormat.parse(value);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
複製代碼
public void annotationTest() {
final String path = "src\\test\\resources\\annotation.csv";
CsvWriteBs.newInstance(path)
.write(buildAnnotationList());
}
複製代碼
其中列表構建:
/** * 構建基於註解的測試列表 * @return 列表 */
private List<UserAnnotation> buildAnnotationList() {
UserAnnotation user = new UserAnnotation();
user.name("你好")
.password("123")
.birthday(new Date());
return Arrays.asList(user);
}
複製代碼
名稱,生日
你好,20190603
複製代碼
public void annotationTest() {
final String path = "src\\test\\resources\\annotation.csv";
List<UserAnnotation> userList = CsvReadBs.newInstance(path)
.read(UserAnnotation.class);
System.out.println(userList);
}
複製代碼
[UserAnnotation{name='你好', password='null', birthday=Mon Jun 03 00:00:00 CST 2019}]
複製代碼
有時候對象中會包含數組、Map、Collection 等常見集合。
爲了存儲的便利性,默認提供集合的相關支持。
特性和普通字段保持一致,若是指定註解轉換,則以註解爲準。
用於演示集合的對象
public class UserCollection {
private String[] arrays;
private LinkedList<String> lists;
private Map<String, String> maps;
private Set<String> sets;
//Getter/Setter/toString()
}
複製代碼
/** * 構建基於集合的測試列表 * @return 列表 * @since 0.0.3 */
private List<UserCollection> buildCollectionList() {
UserCollection user = new UserCollection();
String[] arrays = new String[]{"a", "b", "c"};
LinkedList<String> lists = new LinkedList<>(Arrays.asList(arrays));
Map<String, String> maps = new HashMap<>();
maps.put("key", "value");
maps.put("key2", "value2");
Set<String> sets = new HashSet<>();
sets.add("set1");
sets.add("set2");
user.setLists(lists);
user.setArrays(arrays);
user.setMaps(maps);
user.setSets(sets);
return Arrays.asList(user);
}
複製代碼
public void collectionTest() {
final String path = "src\\test\\resources\\collection.csv";
CsvWriteBs.newInstance(path)
.write(buildCollectionList());
}
複製代碼
arrays,lists,maps,sets
a|b,a|b|c,key2=value2|key=value,set1|set2
複製代碼
public void collectionTest() {
final String path = "src\\test\\resources\\collection.csv";
List<UserCollection> userList = CsvReadBs.newInstance(path)
.read(UserCollection.class);
System.out.println(userList);
}
複製代碼
[UserCollection{arrays=[a, b], lists=[a, b, c], maps={key=value, key2=value2}, sets=[set2, set1]}]
複製代碼
爲了保證 csv 以 ,
分隔的統一性。
集合使用 |
進行分隔,其中 map 的 key/value 分隔,用到了 =
。
在使用時要注意,不要包含上述的符號,不然會出現解析錯亂。
ps: 若是確實用到這些字符,能夠見後面的特殊字符轉義功能。
有時候咱們但願像使用 mongoDB 同樣,很是方便的存取 csv 的嵌套對象。
對於普通的 csv 都沒有實現這個特性,本次作了一個嘗試,支持內嵌對象的存取。
就像 csv 的簡單,須要用到符號 ,
同樣。
內嵌對象爲了避免破壞 csv 的規範,使用了符號 :
。
換言之,也就是對象內容中不能使用這個符號。
後期會針對出現的符號進行轉義,避免這種衝突。
public class UserEntry {
/** * 名稱 */
private String name;
/** * 內嵌的用戶信息 */
@CsvEntry
private User user;
//Getter/Setter/ToString
}
複製代碼
這裏在須要內嵌的對象上使用註解 @CsvEntry
表示須要進行內嵌的對象轉換。
其中 User 對象是原來使用的普通 java 對象
public class User {
private String name;
private int age;
private float score;
private double money;
private boolean sex;
private short level;
private long id;
private char status;
private byte coin;
//Getter/Setter/ToString
}
複製代碼
public void entryTest() {
final String path = "src\\test\\resources\\entry.csv";
CsvWriteBs.newInstance(path)
.write(buildEntryList());
}
複製代碼
負責對象構建代碼,內容以下:
/** * 用戶明細列表 * @return 明細列表 * @since 0.0.5 */
private List<UserEntry> buildEntryList() {
UserEntry userEntry = new UserEntry();
userEntry.name("test");
userEntry.user(buildCommonList().get(0));
return Collections.singletonList(userEntry);
}
複製代碼
private List<User> buildCommonList() {
User user = new User();
short s = 4;
byte b = 1;
user.age(10)
.name("你好")
.id(1L)
.score(60)
.coin(b)
.level(s)
.money(200)
.sex(true)
.status('Y');
return Arrays.asList(user);
}
複製代碼
生成文件效果
name,user
test,你好:10:60.0:200.0:true:4:1:Y:1
複製代碼
如你所見,這裏內嵌對象的屬性使用了 :
進行分隔。
public void entryTest() {
final String path = "src\\test\\resources\\entry.csv";
List<UserEntry> userList = CsvReadBs.newInstance(path)
.read(UserEntry.class);
System.out.println(userList);
}
複製代碼
輸出信息
[UserEntry{name='test', user=User{name='你好', age=10, score=60.0, money=200.0, sex=true, level=4, id=1, status=Y, coin=1}}]
複製代碼
在實際使用中,有時候咱們會用到 ,|:=
。
這幾個被使用的特殊字符。
若是你但願這些特殊的字符被正確的存取,那麼可使用 escape
屬性執行。
原始 | 轉義後 |
---|---|
, |
&CSV_COMMA; |
| |
&CSV_OR; |
: |
&CSV_COLON; |
= |
&CSV_EUQAL; |
下面演示一下如何使用
暫時轉義字符不支持自定義。
public void escapeTest() {
final String path = "src\\test\\resources\\escape.csv";
CsvWriteBs.newInstance(path)
.escape(true)
.write(buildUserEscapeList());
}
複製代碼
name,map,nameList,user
one&CSV_COMMA;one,key&CSV_EUQAL;key=value&CSV_EUQAL;value,one&CSV_OR;one|two&CSV_OR;two,entry&CSV_COLON;name:0:0.0:0.0:false:0:0: :0
複製代碼
其中用到的對象爲:
public class UserEscape {
/** * 使用 , */
private String name;
/** * 使用 map = */
private Map<String, String> map;
/** * 使用 | */
private List<String> nameList;
/** * 使用 : */
@CsvEntry
private User user;
//Getter & Setter & ToString()
}
複製代碼
構建時,特地使用了特殊的字符。
private List<UserEscape> buildUserEscapeList() {
UserEscape escape = new UserEscape();
Map<String, String> map = new HashMap<>();
map.put("key=key", "value=value");
User user = new User();
user.name("entry:name");
escape.name("one,one");
escape.nameList(Arrays.asList("one|one", "two|two"));
escape.map(map);
escape.user(user);
return Collections.singletonList(escape);
}
複製代碼
public void escapeTest() {
final String path = "src\\test\\resources\\escape.csv";
List<UserEscape> userList = CsvReadBs.newInstance(path)
.escape(true)
.read(UserEscape.class);
System.out.println(userList);
}
複製代碼
[UserEscape{name='one,one', nameList=[one|one, two|two], user=User{name='entry:name', age=0, score=0.0, money=0.0, sex=false, level=0, id=0, status= , coin=0}, map={key=key=value=value}}]
複製代碼
支持更多的 java 常見類型。
好比支持用戶自定義轉義字符
支持文件的寫入模式等等。
能夠查看相關代碼。
爲了便於其餘人閱讀和使用,代碼擁有詳細的註釋。