【问题标题】:Unexpected amount of lines when writing to a csv file写入 csv 文件时出现意外的行数
【发布时间】:2017-04-16 14:28:40
【问题描述】:

我的应用程序的一部分通过以下方式将数据写入 .csv 文件:

public class ExampleWriter {

    public static final int COUNT = 10_000;
    public static final String FILE = "test.csv";

    public static void main(String[] args) throws Exception {
        try (OutputStream os = new FileOutputStream(FILE)){         
            os.write(239);
            os.write(187);
            os.write(191);
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));         
            for (int i = 0; i < COUNT; i++) {               
                writer.write(Integer.toString(i));
                writer.newLine();               
            }           
        } catch (IOException e) {                       
            e.printStackTrace();
        }

        System.out.println(checkLineCount(COUNT, new File(FILE)));
    }

    public static String checkLineCount(int expectedLineCount, File file) throws Exception {
        BufferedReader expectedReader = new BufferedReader(new FileReader(file));
        try {
            int lineCount = 0;
            while (expectedReader.readLine() != null) {
                lineCount++;
            }
            if (expectedLineCount == lineCount) {
                return "correct";       
            } else {
                return "incorrect"; 
            }
        }
        finally {
            expectedReader.close();
        }
    }
}

文件将在 excel 中打开,并且所有类型的语言都存在于数据中。 os.write 部分用于在文件前加上字节顺序标记,以启用各种字符。

不知何故,文件中的行数与循环中的计数不匹配,我无法弄清楚如何。对于我在这里做错的任何帮助将不胜感激。

【问题讨论】:

    标签: java csv bufferedwriter byte-order-mark


    【解决方案1】:

    您只需要在打开文件进行输入和计数之前刷新并关闭输出流(强制 fsync)。尝试添加:

    writer.flush();
    writer.close();
    

    在你的 try-block 中。在 main 方法中的 for 循环之后。

    【讨论】:

    • 显然,如果你想在调用 checkLineCount 后重用你的 writer,你不必关闭流。调用 flush() 足以执行 fsync。
    【解决方案2】:

    (作为旁注)。

    请注意,使用 BOM 是可选的,并且(在许多情况下)会降低文件的可移植性(因为并非所有消费应用都能很好地处理它)。它确实保证文件具有广告的字符编码。所以我建议删除 BOM。使用 Excel 时,只需选择文件并选择 UTF-8 作为编码。

    【讨论】:

      【解决方案3】:

      您没有刷新流,请参阅oracle docs 了解更多信息 就是这么说的

      刷新此输出流并强制任何缓冲的输出字节 写出来。 flush 的一般约定是调用它是 指示,如果先前写入的任何字节已被缓冲 输出流的执行,这样的字节应该立即 被写入他们的预定目的地。如果预定目的地 这个流是底层操作提供的抽象 系统,例如一个文件,然后刷新流只保证 先前写入流的字节被传递给 写作操作系统;它不保证它们是 实际写入物理设备,例如磁盘驱动器。

      OutputStream 的 flush 方法什么都不做。

      您需要刷新和关闭流。有两种方法

      1. 手动调用 close() 和 flush()。

      2. 对资源使用 try

      从您的代码中可以看出,您已经实现了资源尝试,而且 BufferedReader 类也实现了 Closeable、Flushable,因此请使用以下代码

      public static void main(String[] args) throws Exception {
              try (OutputStream os = new FileOutputStream(FILE); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))){         
                  os.write(239);
                  os.write(187);
                  os.write(191);
      
                  for (int i = 0; i < COUNT; i++) {               
                      writer.write(Integer.toString(i));
                      writer.newLine();               
                  }          
              } catch (IOException e) {                       
                  e.printStackTrace();
              }
      
              System.out.println(checkLineCount(COUNT, new File(FILE)));
          }
      

      【讨论】:

      • 我选择实现你的版本,因为它通过很好地使用 try-with-resources 看起来更优雅。
      【解决方案4】:

      COUNT 为 1 时,main() 中的代码将写入一个有两行的文件,一行是数据,然后是一个空行。然后你调用 checkLineCount(COUNT, file) 期望它会返回 1 但它返回 2 因为文件实际上有两行。 因此,如果您希望计数器匹配,则不能在最后一行之后写新行。

      【讨论】:

        【解决方案5】:

        (作为另一个旁注)。

        请注意,以您的方式编写 CSV 文件是真的不好的做法。 CSV 并不像乍一看那么容易!所以,除非你真的知道你在做什么(所以要注意所有的 CSV 怪癖),请使用库!

        【讨论】:

          猜你喜欢
          • 2012-05-25
          • 2021-08-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-12-21
          • 2016-08-22
          • 1970-01-01
          相关资源
          最近更新 更多