Java使用Excel的问题:自动跳过空字段中文加拼音和时间处理错误的解决方法

Posted 纵横千里,捭阖四方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java使用Excel的问题:自动跳过空字段中文加拼音和时间处理错误的解决方法相关的知识,希望对你有一定的参考价值。

在Java中解析Excel是很多管理类系统的重要功能, 目前主要有阿里的easyExcel和HSSF两种开源工具,如何使用网上有大量的例子,不再赘述,我们这里看三个我亲身经历的问题:

  1. 自动跳过空字段

  2. 中文内容自动给加了拼音

  3. 时间处理错误。

本文首先分析故障现象或者原因,之后给出解决方案。

1.自动跳过空字段

有些场景下上传的excle文件里有些选项是可选的,用户自然可以不填,但是在使用时会遇到问题。

例如如下的文件:

用户是否签到和是否允许使用pad都是可选的,用户可以根据实际需要填或者不填。但是我们在java 中使用XLSX提供的方法自动读取Excel,如下:

 String fileName = "/Users/liuqingchao/Downloads/five_2.xlsx";

List<List<String>> lists = ExcelUtils.getXLSXDataOfFirstSheet(new FileInputStream(fileName));

结果会出现这样子:

可以看到,debug中很多行是空的,其中序号2、3、4对应的行元素都少了,其中序号2对应的是将文件中的空位置直接跳过了,这是无法接受的,因为后续对应关系都无法确定了。

测试发现,使用XSSF和早期版本的easyExcel都存在该问题。其底层都是基于迭代器方式读的,直接修改其源码不现实,但是可以自己来解析文件,详细实现见asyn-task的getExcelFileFixedZh()方法。

但是使用该方法会导致解析时间时出错。详见下一小节。

2.时间解析错误

我们知道正常的时间格式是”2022-10-31 16:09:13“,但是如果将其放在一个Excel格子里,用户在操作的时候,可能会将其写错,最明显的就是日期和时间之间的空格,如果是中文的空格,用户无法发现的,但是后续处理就会有问题,所以最好将其分开,日期和时间分别用一个格子,例如篇头给出的样子。

但是解析的时候仍然出问题,我们使用如下的代码来解析:

String fileName = "/Users/liuqingchao/Downloads/five_2.xlsx";
List<List<String>> lists= ExcelUtilsFixedZh.getExcelDataRowsInFirstSheet(new FileInputStream(fileName));
        System.out.println(lists);

可以看到日期解析没有问题,但是时间格子对应的全错了:

 

查阅之后发现,如果格子里存的是时间,Excel会自动进行类型转换, 没有日期就从1900年1月1日开始算,此时会出现两种情况,一种是直接将时间转换成”1900.1.1 19:00:00“,另一种是将时间倒退一天给变成”1899.1.1 19:00:00“,而后者转成时间戳就是我们看到的-2209035600000。前者在阿里easy Excel中出现过。

很明显,这都不是我们想要的。

而且,即使此时将List定义为List<List<String>>也无法阻止Excel自动转换时间。

3.中文信息后面带了拼音

该问题是于波发现的,偶尔出现,本人没能复现出来,现象就是中文信息后面自动给加了拼音, 例如本来存的是”否“,但是实际读到的是”否fou“。

4.解决方法

针对上述几个问题,目前发现比较好的方式是:使用easyExcel版本3。并定义一个与一行记录相对应的类。读取的时候,将类对象设置进去,这样可以解决上面提到的三个问题。

具体操作方法见:truman-lecture里AbstractExcelUploadExecutor下的

 List<BatchAddEpisodesExcelVO> records;
        try 
            records = AsynTaskFileUtils.getEasyExcelFileRecord(this.httpClient, innerHost, getParams().getResourceId(), BatchAddEpisodesExcelVO.class);
        

这里的BatchAddEpisodesExcelVO就是与Excel文件完全对应的类对象。获得记录的核心为:

    public static <T> List<T> getEasyExcelFileRecord(HttpClient httpClient, String host, String resourceId, Class clazz) 

.....
            EasyExcel.read(connection.getInputStream(), clazz, new PageReadListener<T>(dataList -> 
                for (T demoData : dataList) 
                    list.add(demoData);
                
            )).sheet().doReadSync();//同步方式
            return list;
.....
    

此时虽然用了回调,但是将其设置为同步模式就可以保证获得全部记录之后才返回。

注意事项

另外注意一点,我们定义BatchAddEpisodesExcelVO这种与excle对应的对象的时候,最好将所有的属性,特别是日期相关务必用String类型,这样可以避免Excel给我们做数据转换。否则使用easyExcle解析时间会出现如下情况:

  1. 如果日期是Date类型,则直接抛异常。

  2. 如果时间是Date类型,则时间仍然是错的。

例如,我们还是使用上的文件,定义一个简单类:

@Getter
@Setter
@EqualsAndHashCode
public class DemoData 
    private String lectureId;
    private Date beginDate;
    private String beginTime;
    private String yunXu;
    private String locale;

然后执行,此时直接抛异常:

com.alibaba.excel.exception.ExcelDataConvertException: Convert data com.alibaba.excel.metadata.data.ReadCellData@4f7479f3 to class java.util.Date error 

如果我们将上面的beginTime的类型改成Date,则时间错误:

 结论:不要让excle给我们做转换,都先用String类型拿到所有数据,我们自己转换。

以上是关于Java使用Excel的问题:自动跳过空字段中文加拼音和时间处理错误的解决方法的主要内容,如果未能解决你的问题,请参考以下文章

index跳过空值提取整行数据

angularjs ng重复svg跳过空值的索引

如何在 Spark JSON 中不跳过空值?

我怎样才能让这个 Map 双函数忽略/跳过空值?

Symfony在序列化期间跳过空值

跳过空XML节点