【问题标题】:Writing korean characters to zipped csv file in Java用Java将韩语字符写入压缩的csv文件
【发布时间】:2016-01-28 17:31:06
【问题描述】:

我正在编写一个我还需要压缩的 csv 文件,并且正在使用 java.util.zip.ZipEntry 和 java.util.zip.ZipOutputStream。

当我在所有列中都有西方字符时,一切都很好,但是当我使用韩语字符时,它无法识别 /n 并且所有内容都出现在同一行上。我将其写为 UTF-8 字符,并希望这涵盖韩语。

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class CreateCSV {

public static void main(String[] args) throws IOException {

    DateTime utcDateTime = new DateTime().toDateTime(DateTimeZone.UTC);
    DateTime newDateTime = utcDateTime.toDateTime();
    DateTimeFormatter dateFormatter = DateTimeFormat.forPattern("yyyyMMdd-HHmmss-SSS-");
    File zipFile = new File("C:/TestCSVKorean/"+ dateFormatter.print(newDateTime) + "Export.zip");

    FileOutputStream fileOutputStream = new FileOutputStream(zipFile);
    // Open up the zipfile and create the csv entry
    ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fileOutputStream));
    zos.putNextEntry(new ZipEntry(dateFormatter.print(newDateTime) + "tics.csv"));
    // The first line of the CSV is a header line
    StringBuffer csvHeader = new StringBuffer(
            "Time,Name,Rev,Appme,EnvName,PlanName,PlanRev,"
                + "Og Name,Op Name,"
              + "TTR,Lat,Bys Rec,Bytt,"
                    + "RPl,Request Method,URI Path,Query String,HTTP Status Code,"
                    + "HTTP Request Headers,User Agent,Request Body,HTTP Response Headers,Response Body\n");
    zos.write(csvHeader.toString().getBytes(), 0, csvHeader.length());

    StringBuffer csvData = new StringBuffer("");

    csvData.append("\"" + newDateTime + "\",\"" +
            "apiName" + "\"," +
            "2.0.0" + ",\"" + 
            "app name" + "\",\"" +
            "env name" + "\",\"" +
            "plan name" + "\"," +
            "2" + ",\"" +
            "dev org name" + "\",\"" +
            "ìº˜ë¦°ë” ëª©ë¡ì¡°íšŒ(ë‚´ 캘린ë”, 구ë…가능한 캘린ë”, 시스템 캘린ë”, ê´€ë¦¬ìž ìº˜ë¦°ë”)" + "\"," +
            "123" + "," +
            "inifd: 334;dshs: 343" + ", " +
            "10" + "," + 
            "33" + ",\"" + 
            "http" + "\",\"" + 
            "GET" + "\",\"" + 
            "/dsfs/sdf/ds" + "\",\"" + "query string" + "\",\"" + 
            "200" + "\",\"" + 
            "jshkshdf" + "\",\"" + 
            "sdjhfks/sdfs/" + "\",\"" +                             
            "jhksdfhks dsfs" + "\",\"" +    
            "dsfsdfs" + "\",\"" +       
            "dsfsfs" + "\"\n"); 

    zos.write(csvData.toString().getBytes("UTF-8"), 0, csvData.length());

    csvData = new StringBuffer("");

    csvData.append("\"" + newDateTime + "\",\"" +
            "apiName" + "\"," +
            "2.0.0" + ",\"" + 
            "app name" + "\",\"" +
            "env name" + "\",\"" +
            "plan name" + "\"," +
            "2" + ",\"" +
            "dev org name" + "\",\"" +
            "ìº˜ë¦°ë” ëª©ë¡ì¡°íšŒ(ë‚´ 캘린ë”, 구ë…가능한 캘린ë”, 시스템 캘린ë”, ê´€ë¦¬ìž ìº˜ë¦°ë”)" + "\"," +
            "123" + "," +
            "inifd: 334;dshs: 343" + ", " +
            "10" + "," + 
            "33" + ",\"" + 
            "http" + "\",\"" + 
            "GET" + "\",\"" + 
            "/dsfs/sdf/ds" + "\",\"" + "query string" + "\",\"" + 
            "200" + "\",\"" + 
            "jshkshdf" + "\",\"" + 
            "sdjhfks/sdfs/" + "\",\"" +                             
            "jhksdfhks dsfs" + "\",\"" +    
            "dsfsdfs" + "\",\"" +       
            "dsfsfs" + "\"\n"); 


    zos.write(csvData.toString().getBytes("UTF-8"), 0, csvData.length());

    zos.close();

}

}

这是我打开 csv 文件时看到的内容:

时间名称 Rev Appme EnvName PlanName PlanRev Og Name Op Name TTR Lat Bys Rec Bytt RPl Request Method URI Path Query String HTTP Status Code HTTP Request Headers User Agent Request Body HTTP Response Headers Response Body
2016-01-28T17:20:56.859Z apiName 2.0.0 应用程序名称环境名称计划名称 2 开发组织名称ÃÅ¡Å'(ë‚´ 캘린ëÂâ€, 구뀦가능Õœ 캘ë °ë€, 시스Ã…Ŕ 캘린ë€, ê´€ë¦Â¬Ã ¬Å¾Â 캘린ë€) 123 inifd: 334;dshs: 343 10 33 http 2016-01-28T17:20:56.859Z apiName 2.0.0 应用程序名称环境名称计划名称 2 开发组织名称캘린ë†목ë¡Âì¡°ÃÅ¡Å'(ë‚´ 캘린ëÂâ€, 구뀦가능՜캘린ë€, 시ìŠ¤Ã…Š“ 캘린ë€, ê´€ë¬ìž ìº˜ë¦°ëÂâ€) 123 inifd: 334;dshs: 343 10 33 http

它将第二行的日期字段粘贴到第一行的请求方法字段中:2016-01-28T17:20:56.859Z

【问题讨论】:

  • 您的 CSV 文件看起来正确。问题在于你如何看待它。你用什么程序打开它?

标签: java csv zip


【解决方案1】:

首先,你应该改掉使用 StringBuffer 的习惯。这是一个过时的类。如果你需要一点一点地追加文字,你通常会改用StringBuilder

但是,在您的情况下,您不需要 StringBuilder 或 StringBuffer。只需使用字符串:

String csvHeader =
        "Time,Name,Rev,Appme,EnvName,PlanName,PlanRev,"
            + "Og Name,Op Name,"
          + "TTR,Lat,Bys Rec,Bytt,"
                + "RPl,Request Method,URI Path,Query String,HTTP Status Code,"
                + "HTTP Request Headers,User Agent,Request Body,HTTP Response Headers,Response Body\n";

还有……

String csvData = "\"" + newDateTime + "\",\"" +
        "apiName" + "\"," +
        "2.0.0" + ",\"" + 
        "app name" + "\",\"" +
        // etc.

其次,注意不要混淆字节数和字符数。当您使用 UTF-8 字符集将字符串转换为字节时,任何不在 US-ASCII 范围 (0-127) 内的字符都将被转换为一个以上的字节。因此,字节数会大于 String 的长度(表示它包含多少个字符,而不是 UTF-8 编码时占用多少字节)。

所以你的写操作应该是:

zos.write(csvData.toString().getBytes("UTF-8"));

第三,我不懂韩语,但我知道韩文字符长什么样,而且我在你的代码中看不到任何字符。我假设你打算这些是韩文:

"ìº˜ë¦°ë” ëª©ë¡ì¡°íšŒ(ë‚´ 캘린ë”, 구ë…가능한 캘린ë”, 시스템 캘린ë”, ê´€ë¦¬ìž ìº˜ë¦°ë”)" + "\"," +

您似乎正在使用 Windows 将每个单独的 UTF-8 字节放入您的字符串中,就好像它是一个字符一样。但是在 Java 中,字节是 not 字符,并且不能与字符互换。

我假设您使用的是 Windows,因为第三个字符,即空格 Unicode 字符 SMALL TILDE,是 \u02dc,通常会占用两个字节,但在 windows-1252 编码中,它是单个字节 0x98。

所以,如果我假设您从韩文字符的 UTF-8 字节中派生出这些字符,那么上述字符串中的前六个字节是:

ec ba 98 eb a6 b0
ì  º  ˜  ë  ¦  °

这些字节是两个韩文字符 U+CE98 和 U+B9B0 的 UTF-8 表示。将这两个字符放在 Java 字符串中的正确方法是:

"\uce98\ub9b0"

如果您的文件中有原始韩文文本,您可以使用每个 JDK 附带的 native2ascii 工具轻松地将整个文本转换为一系列 Java 转义序列,如上一行。这样的命令可能如下所示:

native2ascii -encoding UTF-8 hangul.txt hangulstrings.java

如果您不想费心正确地编写字符串,我不推荐的另一种方法是通过识别强制将当前的“伪字节”字符串解释为 UTF-8 字节它包含表示字节的 Windows-1252 字符并将其还原为这些字节:

zos.write(csvData.getBytes("windows-1252"));

生成的 zip 条目仍将以 UTF-8 编码,因为您的字节是韩文文本的 UTF-8 表示。因此,您需要确保使用能够识别该文件为 UTF-8 的工具打开该文件。

Windows 不是特别擅长识别 UTF-8 文件。记事本尤其不擅长。向 Windows 发出信号表明文件是 UTF-8 文件的一种方法是将字节顺序标记字符写入文件中的第一个字符:

String csvHeader = "\ufeff"
        + "Time,Name,Rev,Appme,EnvName,PlanName,PlanRev,"
        // etc.

【讨论】:

  • 很棒的答案,非常感谢。这有效: zos.write(csvData.toString().getBytes("UTF-8"));
猜你喜欢
  • 1970-01-01
  • 2011-07-04
  • 1970-01-01
  • 2015-07-16
  • 2017-06-03
  • 1970-01-01
  • 1970-01-01
  • 2016-06-15
  • 1970-01-01
相关资源
最近更新 更多