【问题标题】:Java - Prevent PrintWriter to overwrite the entire file everytimeJava - 防止 PrintWriter 每次都覆盖整个文件
【发布时间】:2020-07-06 20:44:08
【问题描述】:

我有一个模拟程序,需要在执行过程中非常频繁地将某些结果写入 csv 文件。我发现 printwriter 出现问题,这会大大减慢程序的运行速度,因为输出文件的大小越来越大(接近 100 万行)。我怀疑它每次都从头开始覆盖整个文件,而每次调用它时我只需要在底部附加一行。下面是写函数相关的代码。

写作功能之一:

    public void printHubSummary(Hub hub, String filePath) {
        
        try {
            StringBuilder sb = new StringBuilder();   
            
            String h = hub.getHub_code();
            String date = Integer.toString(hub.getGs().getDate());
            String time = hub.getGs().getHHMMFromMinute(hub.getGs().getClock());
            String wgt = Double.toString(hub.getIb_wgt());

                    
            sb.append(h+","+date+","+time+","+wgt);
//          System.out.println("truck print line: " + sb);
            FileWriter.writeFile(sb.toString(),filePath);
        }
        
        catch (Exception e) {
            System.out.println("Something wrong when outputing truck summary file!");
            e.printStackTrace();        
        }
    }

文件编写器代码:(应该是问题所在!)

    public static boolean writeFile(String newStr, String filename) throws IOException {
    boolean flag = false;
    String filein = newStr + "\r\n";
    String temp = "";

    FileInputStream fis = null;
    InputStreamReader isr = null;
    BufferedReader br = null;

    FileOutputStream fos = null;
    PrintWriter pw = null;
    try {
        File file = new File(filename);
        fis = new FileInputStream(file);
        isr = new InputStreamReader(fis);
        br = new BufferedReader(isr);
        StringBuffer buf = new StringBuffer();

        for (int j = 1; (temp = br.readLine()) != null; j++) {
            buf = buf.append(temp);
            buf = buf.append(System.getProperty("line.separator"));
        }
        if (buf.length() > 0 && buf.charAt(0) == '\uFEFF') {
            buf.deleteCharAt(0);
        }
        buf.append(filein);

        fos = new FileOutputStream(file);
        byte[] unicode = {(byte)0xEF, (byte)0xBB, (byte)0xBF};
        fos.write(unicode);
        pw = new PrintWriter(fos);
        
        pw.write(buf.toString().toCharArray());
        pw.flush();
        flag = true;
    } catch (IOException e1) {
        throw e1;
    } finally {
        if (pw != null) {
            pw.close();
        }
        if (fos != null) {
            fos.close();
        }
        if (br != null) {
            br.close();
        }
        if (isr != null) {
            isr.close();
        }
        if (fis != null) {
            fis.close();
        }
    }
    return flag;
}

关于代码修改的更新。我已经冻结了重复覆盖整个文件的操作。它似乎解决了这个问题,但写了一段时间它也变慢了。这是编写非常大文件的最佳安排吗?可以进行哪些其他修改以使其更加高效?

public static boolean writeFile1(String newStr, String filename) throws IOException {
        boolean flag = false;
        String filein = newStr + "\r\n";
        String temp = "";

        FileInputStream fis = null;
        InputStreamReader isr = null;
        BufferedReader br = null;

        FileOutputStream fos = null;
        PrintWriter pw = null;
        try {
            File file = new File(filename);
            fis = new FileInputStream(file);
            isr = new InputStreamReader(fis);
            br = new BufferedReader(isr);
            StringBuffer buf = new StringBuffer();

//          for (int j = 1; (temp = br.readLine()) != null; j++) {
//              buf = buf.append(temp);
//              buf = buf.append(System.getProperty("line.separator"));
//          }
//          if (buf.length() > 0 && buf.charAt(0) == '\uFEFF') {
//              buf.deleteCharAt(0);
//          }
            buf.append(filein);

            fos = new FileOutputStream(file,true);
            byte[] unicode = {(byte)0xEF, (byte)0xBB, (byte)0xBF};
            fos.write(unicode);
            pw = new PrintWriter(fos);
            
            pw.write(buf.toString().toCharArray());
            pw.flush();
            flag = true;
        } catch (IOException e1) {
            throw e1;
        } finally {
            if (pw != null) {
                pw.close();
            }
            if (fos != null) {
                fos.close();
            }
            if (br != null) {
                br.close();
            }
            if (isr != null) {
                isr.close();
            }
            if (fis != null) {
                fis.close();
            }
        }
        return flag;
    }

【问题讨论】:

  • 我不确定我是否理解。每次写入新行时,您是否都在读取整个文件?然后再把整个文件写出来?
  • 只要让你的一个 printWriter 活着,不要在任何时候调用这个函数时创建一个新的,一遍又一遍地使用它。或者以“追加”模式打开文件,只写入新行。
  • 我怀疑是这样,这可能是原因。
  • @luk2302 你能建议对代码的具体修改吗?
  • 我建议阅读一些文档。我看不出有任何理由将整个文件读入一个字符串,然后再将其写回。如果这是一个长时间运行的操作,我会小心保持文件打开,因为丢失数据的可能性会增加。

标签: java csv printwriter


【解决方案1】:

为FileOutputStream构造函数提供第二个参数来指定是否使用追加模式,这将添加到文件的末尾而不是覆盖它。

fos = new FileOutputStream(file, true);

或者,您可以在附加模式下创建一个静态 PrintWriter,这可能会更快,因为它可以减少垃圾收集。

【讨论】:

  • 加上“true”后变得更慢了,为什么?
  • @Jack 我不确定。但是它有效吗?
  • 它变得更加缓慢。几乎卡住了!
  • @Jack 进行此修改后,不要读取整个文件并将其写回,因为不再需要。只写你真正需要添加的部分,即只写filein。
  • 请在编辑中查看我的更新。现在它以更快的速度运行。但这是编写非常大文件的最佳安排吗?可以进行哪些其他修改以使其更加高效?
【解决方案2】:

使用更丰富的文件/路径/Java NIO2:下面的代码至少需要Java 7。

Path path = Paths.get(filename);
try (BufferedWriter bw = Files.newBufferedWriter(
      path, StandardCharsets.UTF_8, StandardOpenOption.APPEND, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
  bw.append(filein);
  bw.newLine();
}

您的提示是StandardOpenOption。

您可能需要在之前编写一些额外的代码来编写 Unicode 部分(并修复 StandardCharsets.UTF_8):

if (Files.notExists(path)) {
  Files.write(path, new byte[] {(byte)0xEF, (byte)0xBB, (byte)0xBF});
}

另外,尽量不要在本地方法中使用StringBuffer,使用StringBuilder:大多数时候你不需要同步。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-18
    • 2022-01-16
    • 1970-01-01
    • 2015-08-26
    • 2014-05-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多