CSV踩坑大全

之前做了好几个关于CSV文件下载的接口,真的是踩坑无数啊!今天有时间在这里把所踩的坑都总结一下,避免以后出现问题。

众所周知,CSV其实就是一个文本文件,占用的空间很小,行与行之间使用 ','分割,可以用Office打开,那这么简单的一个东西会有什么问题呢?

数字变成科学计数法

  • 如果数字超过15位后,它的第16位及以后的位数都会变成0,怎么解决呢?那就是把他变成文本。
	/**
     * 字符串处理和赋值
     *
     * @return
     */
    public String handlerStringAndConvert(String string) {
        return StringUtil.isEmpty(string) ? "" : string.concat("\t");
    }

字符串错乱,由于文本之中存在逗号

因为CSV文件默认是以逗号分割的,如果你的文本中存在逗号,则就会自动分割,怎么办呢?就是要把逗号进行转义。

	/**
     * 字符串中间携带逗号,csv文件需要进行转义
     *
     * @param str
     * @return
     */
    public String handleComma(String str) {
        String handleString = str;
        if (StringUtil.isNotEmpty(str)) {
            if (str.contains(",") || str.contains(",")) {
                if (str.contains("\"")) {
                    handleString = str.replace("\"", "\"\"");
                }
                str = "\"" + handleString + "\"";
            }
        }
        return str;
    }

行内换行

有的时候我们需要在CSV的内部展示多行数据,怎么办呢?只需要在尾部追加换行符即可。

String str = "你的文本".concat("\r\n");

形如数字007变成了7,数字前面的0不见了

这是软件自设的问题,正常我们在输入数字前加上 ’ ,然后我们输入的数据就会我们正常输入的内容,为了避免这种情况的发生,我们只需要在文本的后面追加一个制表符即可。

	/**
     * 字符串处理和赋值
     *
     * @return
     */
    public String handlerStringAndConvert(String string) {
        return StringUtil.isEmpty(string) ? "" : string.concat("\t");
    }

这里和上面保持一致,但是这么做是完美的嘛?如果自己测试会发现,你虽然成功的让自己的文本变成了想要的样子,但是如果你用记事本或者NotePad++打开,你会发现每一个带上这个方法的字段后面都会带上一个tab,看着就像是一个空格,如果删除你的数字依旧会丢失前面的0。这个问题在网上找了很多答案,几乎都是

String str = str + ((char)(9)).ToString();

但是我试了,我连大写的ToString在哪我都不知道,所以呢?如果你想要保住0就需要加上"\t",不想保持0那就可以不加,如果二者都要,你可能得疯掉,搞了好久,都搞不定。

最后推荐一个方式生成CSV,基于注解实现CSV的导出。

可以参考:https://blog.csdn.net/qq_31457665/article/details/88982565

这个工具呢,有一个问题,就是生成的文本时不带双引号的,也就是说你用记事本打开,不会有引号的存在,如果需要,我做了一些调整:

//定义双引号
private static final char DOUBLE_QUOTES = '"';

/**
     * 拼接CVS表格一行数据
     * 在lineStrBuilder.append(dataColumn);前后加上双引号
     * @param filedAnnotationMap
     * @return
     */
    private static <T> String getCsvOneLine(Map<String, CsvColumn> filedAnnotationMap, T lineDate, Class<?> dataClass) {
        StringBuilder lineStrBuilder = new StringBuilder();
        //1循环data,一个obj代表一行
        filedAnnotationMap.forEach((key, value) -> {
            //2循环filedAnnotationMap,一个Entity代表一列的数据
            try {
                Field field = dataClass.getDeclaredField(key);
                field.setAccessible(true);
                String dataColumn = Optional.ofNullable(field.get(lineDate)).orElse("").toString();
                //3解析SpringEL表达式,处理自定义的输出格式需求
                if (StringUtils.isNotEmpty(value.springEL())) {
                    dataColumn = getSpringELValue(value.springEL(), lineDate);
                }
                //4转译处理(放在el解析后,防止el解析逻辑出现幺蛾子(EL转译后出现CSV逻辑符号))
//                lineStrBuilder.append(symbolTranslation(dataColumn));
                lineStrBuilder.append(DOUBLE_QUOTES);
                lineStrBuilder.append(dataColumn);
                lineStrBuilder.append(DOUBLE_QUOTES);
                lineStrBuilder.append(CSV_COLUMN_SEPARATOR);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                log.error("CsvUtil根据反射操作属性异常,异常信息{0}", e);
            }
        });
        return lineStrBuilder.append(CSV_RN).toString();
    }
/**
* 自己写一个拼接,每个元素在生成的时候,前后加上双引号即可
*/
private static String initCsvHeaderLine(Map<String, CsvColumn> filedAnnotationMap) {
        final String csvHeaderLineStr = StringUtils.join(filedAnnotationMap.values().stream()
                .map(CsvColumn::title).map(CsvUtil2::join).collect(Collectors.toList()), CSV_COLUMN_SEPARATOR);
        csvHeadLineCache.put(Integer.toHexString(filedAnnotationMap.hashCode()), csvHeaderLineStr);
        return csvHeaderLineStr;
    }

    private static String join(String string){
        return DOUBLE_QUOTES + string + DOUBLE_QUOTES;
    }

做完CSV下载的接口之后,又学到了一点新知识,嘻嘻!!

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值