业务背景

我们经常需要去解析某些文件生成 csv, 比如数据库数据生成 csv。

下面记录一下采坑记录,以后可以借鉴。

不同的系统

需要解析一个文件,平时使用 windows 系统。

结果收到的是 *.tar.gz 文件。

linux 解压方式

你可以把文件上传到 linux 来加压。

流程如下:

下载 putty

putty-64bit-0.71-installer.msi

然后安装,我们需要用到一个命令 PSCP

复制到 linux

pscp *.tar.gz user@address:/path/

可以将文件上传到对应机器的用户目录下面。

解压

$   gunzip  *.tar.gz

or

$   tar -xzvf *.tar.gz

windows 解压方式

使用 7zip 解压两次即可。

gzip=>tar=>file

java 代码解压方式

当然,你可以使用 java 代码直接解压。

此处不再赘述。

需求梳理

文件

文件是什么样的?

有没有对应的文档?

使用者希望以什么样的形式生成?

这些问题,需要在解析之前就确认好,不然也是白费力气。

设计思路

确认好用户是想要 csv 还是 excel。

如果是 excel 则需要创建对应的对象,你可以参考我原来写的

IExcel

如果是 csv 就可以自己手动去写。

不过写的过程中,还是遇到了不少的坑。

CSV 采坑记录

最佳实践

Writer writer = new FileWriter(targetFileName);
// 手动加上BOM标识
writer.write(new String(new byte[] { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF }));
for(int i = 0; i < allLines.size(); i++) {
    writer.write(allLines.get(i));
    writer.write(System.getProperty("line.separator"));
}
writer.flush();

windows 下的 bom

如果你只是单纯的生成 csv+中文表头。

双击打开的时候,中文可能会乱码。

但是你用存文本打开,发现文件没有问题。

因为 windows excel 需要借助 bom 去识别文件,所以需要这一行:

writer.write(new String(new byte[] { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF }));

文件生成不全

一开始测试的时候,文件没有生成全。debug 发现内容并没有问题。

感觉莫名其妙。后来想起来,可能是缓冲区造成的。

所以需要这一句

writer.flush();

文件生成报错

有时候目标文件可能不是 utf-8 的,你可能会遇到下面的报错:

java.nio.charset.MalformedInputException: Input length = 1

调整下文件读取的代码即可:

比如是 gbk 的话

Charset charset = Charset.forName("GBK");
List<String> stringList = Files.readAllLines(filePath, charset);

excel 生成的一些思考

通畅情况下,excel 根据 bean 去生成,就要写很多 line==>field==>对象 的代码。

后来想想,实际上没有必要。

因为对象只是一个中间临时变量。

字段生成

可以直接根据行的解析内容,生成对象字段。下标和名称一一对应。

private String _0;
private String _1;
private String _2;
...

字段设置

可以直接根据发射设置字段的值,这样反而方便很多。

文件遍历的方法

jdk7 的强大功能

有时候你可能需要遍历某一个文件夹下的所有文件,jdk7 以后这一切都变得非常简单。

示例代码

@Override
public List<Path> getAllMatch() throws IOException {
    Path rootPath = Paths.get(Visit.DEFAULT_ROOT_PATH);
    List<Path> stringList = new ArrayList<>();
    Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            boolean condition = 你的过滤条件;
            if(condition) {
                stringList.add(file);
            }
            return FileVisitResult.CONTINUE;
        }
        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
            return FileVisitResult.CONTINUE;
        }
    });
    return stringList;
}

代码的灵活性

有时候可能使用者希望文件是单独的,有时候希望放在一起。

所以即使在写最基本的代码,你也应该考虑自己代码实现的灵活性。

一些思考

compress

压缩算法平时太依赖于工具,想想这并不是好事情。

比如我想把某些文件夹下的所有文件解密。

那么对于解压文件的依赖就难以做到。

或者说有时候跨平台的文件解压,获取压缩文件内容,等等。

所以我需要一个自己的 compress 代码仓库,用于学习记录。

参考资料

我的Java开发学习之旅——>Java NIO 报java.nio.charset.MalformedInputException: Input length = 1异常