【问题标题】:Java - Cannot read special characters correctly in BufferedReaderJava - 无法在 BufferedReader 中正确读取特殊字符
【发布时间】:2016-11-16 14:07:30
【问题描述】:

我创建了从 csv 文件中读取数据的代码。但是,我无法处理特殊字符,例如 £。

例如,My Base Cost (K£) 被读取为My Base Cost (K£)

我能做些什么来纠正这个问题?

public void parseCSVFile(String filename){

     try {
            br = new BufferedReader(new FileReader(csvDirectory + filename));

            while ((parsedLines = br.readLine()) != null) {

                String[] parsedData = parsedLines.split(csvSplitByComma);

                entireFeed.add(parsedData[0]);
                entireFeed.add(parsedData[1]);

                System.out.println(parsedData[0]);
                System.out.println(parsedData[1]);

                it = entireFeed.iterator();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
}

【问题讨论】:

标签: java bufferedreader


【解决方案1】:

编写 CSV 的代码已损坏。它在 UTF-8 中三重编码它编写的文本。

在 UTF-8 中,ASCII 字符(代码点 0–127)表示为单个字节;他们不需要编码。这就是为什么只有£ 受到影响。

£ 需要 UTF-8 中的两个字节。这些字节是:0xc2、0xa3。如果编写 CSV 文件的代码正确使用了 UTF-8,则该字符将在文件中显示为这两个字节。

但是,显然,某处的某些代码使用单字节字符集(如 ISO-8859-1)读取文件,导致每个单独的字节被视为其自己的字符。然后它使用 UTF-8 对这些单个字符进行编码。意思是,它采用了 { 0xc2, 0xa3 } 字节并以 UTF-8 编码 每个字节。这反过来又产生了这些字节:0xc3、0x82、0xc2、0xa3。 (具体:U+00C2字符在UTF-8中表示为0xc3 0x82,U+00A3字符在UTF-8中表示为0xc2 0xa3。)

然后,在那之后的某个时间,同样的事情又发生了这四个字节是使用单字节字符集读取的,每个字节都被视为自己的字符,而这四个字符中的每一个以 UTF-8 编码,产生 8 个字节:0xc3、0x83、0xc2、0x82、0xc3、0x82、0xc2、0xa3。 (并非每个字符在编码为 UTF-8 时都转换为两个字节;只是碰巧所有这些字符都是。)

这就是为什么当您使用 ISO-8859-1 字符集读取文件时,每个字节都会得到一个字符:

à   ƒ      ‚   à   ‚      £
c3  83  c2  82  c3  82  c2  a3

(从技术上讲, 实际上是 U+201A“单低 9 引号”,但许多每字符一个字节的 Windows 字体历来在位置 0x82 处具有该字符。)

那么,既然我们知道了您的文件是如何形成的,那么您会怎么做呢?

首先,不要再让事情变得更糟了。如果您可以控制正在写入文件的代码,请确保该代码明确指定了用于读取和写入的字符集。 UTF-8 几乎总是最好的选择,至少对于任何主要使用西方字符的文件而言。

第二,如何修复文件?恐怕无法自动检测到这种错误编码,但至少在这个文件的情况下,您可以对其进行三次解码。

如果文件不是很大,你可以把它全部读入内存:

byte[] bytes = Files.readAllBytes(Paths.get(csvDirectory, filename));
// First decoding: £ is represented as four characters
String content = new String(bytes, "UTF-8");

bytes = new byte[content.length()];
for (int i = content.length() - 1; i >= 0; i--) {
    bytes[i] = (byte) content.charAt(i);
}
// Second decoding: £ is represented as two characters
content = new String(bytes, "UTF-8");

bytes = new byte[content.length()];
for (int i = content.length() - 1; i >= 0; i--) {
    bytes[i] = (byte) content.charAt(i);
}
// Third decoding: £ is represented as one character
content = new String(bytes, "UTF-8");

br = new BufferedReader(new StringReader(content));

// ...

如果它是一个大文件,你会希望将每一行读取为字节:

try (InputStream in = new BufferedInputStream(
    Files.newInputStream(Paths.get(csvDirectory, filename)))) {

    ByteBuffer lineBuffer = ByteBuffer.allocate(64 * 1024);

    int b = 0;
    while (b >= 0) {
        lineBuffer.clear();

        for (b = in.read();
             b >= 0 && b != '\n' && b != '\r';
             b = in.read()) {

            lineBuffer.put((byte) b);
        }

        if (b == '\r') {
            in.mark(1);
            if (in.read() != '\n') {
                in.reset();
            }
        }

        lineBuffer.flip();
        byte[] bytes = new byte[lineBuffer.limit()];
        lineBuffer.get(bytes);

        // First decoding: £ is represented as four characters
        String parsedLine = new String(bytes, "UTF-8");

        bytes = new byte[parsedLine.length()];
        for (int i = parsedLine.length() - 1; i >= 0; i--) {
            bytes[i] = (byte) parsedLine.charAt(i);
        }
        // Second decoding: £ is represented as two characters
        parsedLine = new String(bytes, "UTF-8");

        bytes = new byte[parsedLine.length()];
        for (int i = parsedLine.length() - 1; i >= 0; i--) {
            bytes[i] = (byte) parsedLine.charAt(i);
        }
        // Third decoding: £ is represented as one character
        parsedLine = new String(bytes, "UTF-8");

        // ...
    }
}

【讨论】:

  • 感谢您的解释,我错在哪里是有道理的。我已经更正了我的代码,它现在按预期工作。
【解决方案2】:

似乎是编码问题。找出您的文件被编码的字符集。假设编码是 UTF-8 你可以做这样的事情

new BufferedReader(new InputStreamReader(new FileInputStream("my/path/to/File"), "UTF-8"));

这应该可以解决您的问题

【讨论】:

    猜你喜欢
    • 2012-03-06
    • 1970-01-01
    • 2014-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多