天天看點

csv檔案導出注意事項

一、中文亂碼問題

預設情況下,在windows上用excel打開csv檔案時,并不是按utf-8碼解析的,就算代碼裡設定了寫入字元串為utf-8字元集,也可能亂碼。

csv檔案導出注意事項

需要在檔案頭寫入幾個特殊的位元組,做為utf-8的BOM頭。

/**
         * utf-8的bom頭
         */
        byte[] UTF8_HEADER_BOM = new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};           

複制

建議導出後,用一些編輯工具檢視16進制格式,确認前3個位元組是否ef bb bf

csv檔案導出注意事項

二、大數字被顯示為科學計數法的問題

一些産品編碼,比如100000000001,打開後,會變成:

csv檔案導出注意事項

可在文本後加\t解決

三、海量資料寫入時如何避免OOM

通常我們會把内容先拼成一個String,然後一次性寫入,如果資料量巨大,比如上千萬,一次性拼接很容易造成OOM。可以借用記憶體映射(NIO中的技術)優化。

RandomAccessFile file = new RandomAccessFile(csvFileName, "rw");
        FileChannel channel = file.getChannel();
        MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, file.length(), UTF8_HEADER_BOM.length);
        mappedByteBuffer.put(UTF8_HEADER_BOM);           

複制

完整示例:

/**
     * csv寫入示例(菩提樹下的楊過 http://yjmyzz.cnblogs.com)
     *
     * @throws IOException
     */
    private static void testCsv() throws IOException {
        /**
         * utf-8的bom頭
         */
        byte[] UTF8_HEADER_BOM = new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};

        String csvFileName = "D:\\temp\\test.csv";
        FileUtils.deleteQuietly(new File(csvFileName));

        RandomAccessFile file = new RandomAccessFile(csvFileName, "rw");
        FileChannel channel = file.getChannel();


        byte[] header = "編号,品名,時間戳\n".getBytes("UTF-8");

        //寫入utf8的bom頭,防止打開csv時顯示亂碼
        MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, file.length(), UTF8_HEADER_BOM.length);
        mappedByteBuffer.put(UTF8_HEADER_BOM);

        //寫入标題欄
        mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, file.length(), header.length);
        mappedByteBuffer.put(header);

        //分批寫入記錄(每批1000條)-防止OOM
        long timestamp = System.currentTimeMillis();
        for (int i = 1; i <= 100; i++) {
            StringBuilder sb = new StringBuilder();
            for (int j = 1; j <= 1000; j++) {
                sb.append(i * j + "\t,");
                sb.append("産品" + j + ",");
                sb.append(timestamp + "\t\n");
            }
            byte[] data = sb.toString().getBytes("UTF-8");
            mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, file.length(), data.length);
            mappedByteBuffer.put(data);
        }

        //關閉通道
        channel.close();
    }           

複制

導出效果:

csv檔案導出注意事項