如何使用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
相关产品推荐
相关产品推荐

