we usually export datas with *.cvs file to custom.
we usually import datas with *.cvs file from custom.
so we should have a good library to read and write cvs file.
高内聚,低耦合,可扩展,使用方便.
i choose Google Code [JCSV]
[img]http://dl2.iteye.com/upload/attachment/0104/7887/94b31a76-d460-3c18-ae3e-0a083fb7a3f8.png[/img]
use annotation to assign which column should be apply to Java Ojbect field.
public class UserDO {
@MapToColumn(column=0)
private String userId;
@MapToColumn(column=1)
private String lastName;
@MapToColumn(column=2)
private String middleName;
@MapToColumn(column=3)
private String firstName;
@MapToColumn(column=4)
private String email;
@MapToColumn(column=5)
private String phone;
@MapToColumn(column=6)
private String currentPassword;
@MapToColumn(column=7,type=Date.class)
private Date passwordExpirationDate;
@MapToColumn(column=8)
private String active;
@MapToColumn(column=9)
private String extUserId;
@MapToColumn(column=10,type=Date.class)
private Date lastLogin;
@MapToColumn(column=11)
private String timeZoneId;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
...
simple type, java can auto-box, but if the Date is special, may be 'mm/dd/yyyy', may be 'yyyy-mm-dd'. may be null, or "". we can extend this function
package com.googlecode.jcsv.reader.dataobject.typeprocessor;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import com.googlecode.jcsv.annotations.ValueProcessor;
public class MiniDateProcessor implements ValueProcessor<Date> {
private final DateFormat sdf;
public MiniDateProcessor(DateFormat sdf) {
this.sdf = sdf;
}
/**
* Parses the value as a date, using the given DateFormat instance.
*
* @param value
* the input string
* @return Date the date
* @throws IllegalArgumentException
* if the input String can not be parsed by the given
* DateFormat instance
*/
@Override
public Date processValue(String value) {
Date result = null;
try {
if(value==null||"".equals(value)){
return null;
}else{
result = sdf.parse(value);
}
} catch (ParseException pe) {
throw new IllegalArgumentException(value + " can not be parsed as a date", pe);
}
return result;
}
}
this is main call, it is easy to integrate which format of cvs, and which Java Object you want to convert to.
/**
* MapToColumn
* @throws IOException
*/
public void testReadToAnnotationJavaObject() throws IOException {
ValueProcessorProvider provider = new ValueProcessorProvider();
provider.removeValueProcessor(Date.class);
provider.registerValueProcessor(Date.class, new MiniDateProcessor(new SimpleDateFormat("dd.mm.yyyy HH:mm:ss")));
CSVEntryParser<UserDO> entryParser = new AnnotationEntryParser<UserDO>(UserDO.class, provider);
CSVReader<UserDO> csvPersonReader = new CSVReaderBuilder<UserDO>(new FileReader("./users.csv")).strategy(CSVStrategy.UK_DEFAULT)
.entryParser(entryParser).build();
List<UserDO> datas = csvPersonReader.readAll();
for (UserDO data : datas) {
System.out.println(print(data));
}
}
At this step, All *.CVS's column is transform to Java Object with actual type, may be any body will be said, not all csv is datas, the first line may be the header. JCVS have consider this, so provider a method:
//skip first line;
List<String> headers = csvPersonReader.readHeader();
this can skip first header line, parse all datas to List<UserDO>, it is more easy and simple to use, and you can extends ValueProcessor as above.
there is an easy way to use JCSV is no convert, just help you to split one line to String[].
public void testReadToJavaString() throws IOException {
CSVReader<String[]> csvParser = CSVReaderBuilder
.newDefaultReader(new FileReader("./users.csv"));
List<String[]> datas = csvParser.readAll();
for (String[] data : datas) {
StringBuffer line = new StringBuffer();
boolean first = true;
for (String d : data) {
if (first) {
line.append(d);
first = false;
} else {
line.append("," + d);
}
}
System.out.println(line.toString());
}
}
this method is very easy, you can find in home page of JCSV, the sources code is very easy, can parser large file, don't be care of speed, it just readline one times
split line to Array:
@Override
public List<String> tokenizeLine(String line, CSVStrategy strategy, BufferedReader reader) throws IOException {
final char DELIMITER = strategy.getDelimiter();
final char QUOTE = strategy.getQuoteCharacter();
final char NEW_LINE = '\n';
final StringBuilder sb = new StringBuilder(30);
final List<String> token = new ArrayList<String>();
line += NEW_LINE;
State state = State.NORMAL;
int pointer = 0;
while (true) {
final char c = line.charAt(pointer);
switch (state) {
case NORMAL:
if (c == DELIMITER) {
token.add(sb.toString());
sb.delete(0, sb.length());
} else if (c == NEW_LINE) {
if (!(token.size() == 0 && sb.length() == 0)) {
token.add(sb.toString());
}
return token;
} else if (c == QUOTE) {
if (sb.length() == 0) {
state = State.QUOTED;
} else if (line.charAt(pointer + 1) == QUOTE && sb.length() > 0) {
sb.append(c);
pointer++;
} else if (line.charAt(pointer + 1) != QUOTE) {
state = State.QUOTED;
}
} else {
sb.append(c);
}
break;
case QUOTED:
if (c == NEW_LINE) {
sb.append(NEW_LINE);
pointer = -1;
line = reader.readLine();
if (line == null) {
throw new IllegalStateException("unexpected end of file, unclosed quotation");
}
line += NEW_LINE;
} else if (c == QUOTE) {
if (line.charAt(pointer + 1) == QUOTE) {
sb.append(c);
pointer++;
break;
} else {
state = State.NORMAL;
}
} else {
sb.append(c);
}
break;
}
pointer++;
}
}
this is very clear, support "," to split line and can contains "".
also it can support \"\", can read the contex contains two Quoteds.