You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Writer实现多行CSV文件写入并解决覆盖、空指针及数据重复/顺序异常问题

如何使用Writer实现多行CSV文件写入并解决覆盖、空指针及数据重复/顺序异常问题

嘿,我来帮你一步步搞定这些CSV写入的坑——文件被逐行覆盖、空指针异常、数据重复/顺序错位,这些都是文件操作和数据库游标处理里的常见问题,咱们逐个击破!

一、先解决核心的「文件被逐行覆盖」问题

你原来的代码每次循环都重新打开文件写入,默认是覆盖模式,所以最后只剩最后一行。这里给你两种靠谱的解决方法:

方法1:用追加模式打开Writer,逐行写入

把Writer的初始化移到循环外面,打开输出流时指定append=true,这样每次写入都会追加到文件末尾。记得把整个循环包在try-with-resources里,确保Writer能正确关闭:

public void toSVC(addActivity c) throws IOException, InstantiationException {
    namet = c.name.toString();
    dbsa = Display.getInstance().openOrCreate("pandr.db");
    String rd = "SELECT * FROM pandr WHERE name ='" + namet + "'";
    cursa = dbsa.executeQuery(rd);
    
    // 提前创建文件对象,避免循环内重复创建
    String flname = "/" + namet + ".csv";
    FileSystemStorage fs = FileSystemStorage.getInstance();
    File sendFile = new File(fs.getAppHomePath() + flname);

    // 用追加模式打开Writer,将整个循环包裹在try-with-resources中
    try (Writer w = new OutputStreamWriter(fs.openOutputStream(sendFile, true))) {
        rn = 1;
        // 用游标判断是否有下一行,比固定循环次数更可靠
        while (cursa.next()) {
            Row currentRow = cursa.getRow();
            trecno = rn.toString();
            tname = currentRow.getString(1);
            tpos = currentRow.getInteger(2);
            posv = tpos.toString();
            ttr = currentRow.getInteger(3);
            trv = ttr.toString();
            tbloc = currentRow.getString(4);
            tlwloc = currentRow.getString(5);
            tcfloc = currentRow.getString(6);
            trwloc = currentRow.getString(7);
            tamloc = currentRow.getString(8);
            tcmloc = currentRow.getString(9);
            tdmloc = currentRow.getString(10);
            tlcbloc = currentRow.getString(11);
            trcbloc = currentRow.getString(12);
            tlfbloc = currentRow.getString(13);
            trfbloc = currentRow.getString(14);
            tgloc = currentRow.getString(15);
            
            String d = trecno + "," + tname + "," + posv + "," + trv + "," + tbloc + "," + tlwloc + "," + tcfloc + "," + trwloc + "," + tamloc + "," + tcmloc + "," + tdmloc + "," + tlcbloc + "," + trcbloc + "," + tlfbloc + "," + trfbloc + "," + tgloc + "\n";
            w.write(d);
            rn++;
        }
    } catch (Exception e) {
        Log.p("Error " + e);
    } finally {
        // 确保数据库资源被正确关闭
        if (cursa != null) cursa.close();
        if (dbsa != null) dbsa.close();
        Util.cleanup(os);
    }
    new FinishBanner();
}

方法2:用StringBuilder拼接所有内容,一次性写入

这种方法适合数据量不大的场景,减少文件IO次数,也从根源避免覆盖问题:

public void toSVC(addActivity c) throws IOException, InstantiationException {
    namet = c.name.toString();
    dbsa = Display.getInstance().openOrCreate("pandr.db");
    String rd = "SELECT * FROM pandr WHERE name ='" + namet + "'";
    cursa = dbsa.executeQuery(rd);
    
    StringBuilder sb = new StringBuilder(); // 提前初始化,避免空指针
    rn = 1;
    
    // 遍历游标拼接所有内容
    while (cursa.next()) {
        Row currentRow = cursa.getRow();
        trecno = rn.toString();
        tname = currentRow.getString(1);
        tpos = currentRow.getInteger(2);
        posv = tpos.toString();
        ttr = currentRow.getInteger(3);
        trv = ttr.toString();
        tbloc = currentRow.getString(4);
        tlwloc = currentRow.getString(5);
        tcfloc = currentRow.getString(6);
        trwloc = currentRow.getString(7);
        tamloc = currentRow.getString(8);
        tcmloc = currentRow.getString(9);
        tdmloc = currentRow.getString(10);
        tlcbloc = currentRow.getString(11);
        trcbloc = currentRow.getString(12);
        tlfbloc = currentRow.getString(13);
        trfbloc = currentRow.getString(14);
        tgloc = currentRow.getString(15);
        
        sb.append(trecno).append(",")
          .append(tname).append(",")
          .append(posv).append(",")
          .append(trv).append(",")
          .append(tbloc).append(",")
          .append(tlwloc).append(",")
          .append(tcfloc).append(",")
          .append(trwloc).append(",")
          .append(tamloc).append(",")
          .append(tcmloc).append(",")
          .append(tdmloc).append(",")
          .append(tlcbloc).append(",")
          .append(trcbloc).append(",")
          .append(tlfbloc).append(",")
          .append(trfbloc).append(",")
          .append(tgloc).append("\n");
        rn++;
    }
    
    // 一次性写入文件
    String flname = "/" + namet + ".csv";
    FileSystemStorage fs = FileSystemStorage.getInstance();
    File sendFile = new File(fs.getAppHomePath() + flname);
    Util.writeStringToFile(sendFile, sb.toString());
    
    // 关闭资源
    cursa.close();
    dbsa.close();
    Util.cleanup(os);
    new FinishBanner();
}

二、解决「NullPointerException」问题

你之前遇到的空指针,大概率是这两个原因:

  • StringBuilder未初始化:如果没执行new StringBuilder()就直接调用append(),必然会空指针,上面的代码已经提前做了初始化。
  • Writer作用域错误:如果把Writer放在循环外,但用try-with-resources包裹单次写入,Writer会被提前关闭,第二次写入就会触发空指针。一定要把整个循环放在try-with-resources里,保证Writer在循环全程处于打开状态。

三、解决「数据重复/顺序异常」问题

从你的描述来看,问题出在数据库游标处理:

  • 原来的代码一开始没调用cursa.next()就直接读取currentRow,导致第一次循环读取的是游标初始位置的行,后续循环才移动游标,就会出现第一行重复、顺序错位的情况。
  • 改用while (cursa.next())遍历是最可靠的:这个条件会先移动游标到下一行,判断是否存在有效数据,再读取内容,完全不会出现重复或漏行的问题。
  • 之前用固定次数for (r = 1; r < x; r++)循环,x的值来自数据库字段,可能和实际查询到的行数不匹配,导致循环次数错误,用游标自带的next()判断更准确。

最后给你两个小建议:

  • 如果CSV字段可能包含逗号、引号等特殊字符,建议用专门的CSV库(比如Apache Commons CSV)处理,避免手动拼接出现格式错误。
  • 数据库查询用字符串拼接容易引发SQL注入,建议改用参数化查询代替"SELECT * FROM pandr WHERE name ='" + namet + "'"。

备注:内容来源于stack exchange,提问作者curtjacobs1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 07:14:30