【问题标题】:How do I write a character at codepoint 80 to a file in Windows-1252?如何将代码点 80 处的字符写入 Windows-1252 中的文件?
【发布时间】:2020-03-09 02:17:44
【问题描述】:

我正在尝试将字节写入 windows-1252 字符集中的文件。下面的示例将浮点数的原始字节写入文件,这与我在实际程序中所做的类似。

在给出的示例中,我将 1.0f 的原始十六进制写入 test.txt。由于 1.0f 的原始十六进制是 3f 80 00 00,我希望得到 ?€(NUL)(NUL),正如我在 Windows 1252 Wikipedia article 中看到的那样,0x3f应该对应'?',0x80应该对应'',0x00 是 'NUL'。在我真正尝试写入文件之前,一切都很好;那时,我在控制台上得到一个 java.nio.charset.UnmappableCharacterException ,在程序因该异常停止后,该文件只有一个“?”在里面。完整的控制台输出在下面的代码下方。

看起来 Java 认为代码点 0x80 在 windows-1252 代码页中不可映射。然而,这似乎不对——所有代码点都应该映射到该代码页中的实际字符。问题肯定出在代码点 0x80 上,好像我尝试使用 0.5f (3f 00 00 00) 很高兴写 ?(NUL)(NUL )(NUL) 到文件中,并且不抛出异常。尝试其他代码页似乎也不起作用。查看 Java 语言 here 支持的关键编码,只有 UTF 系列不会给我例外,但由于它们的编码,它们在实际文件中没有给我代码点 0x80

我将尝试只使用字节来代替,这样我就不必担心字符串编码,但是有人能告诉我为什么下面的代码给了我它的异常吗?

代码:

import java.io.IOException;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;

public class CharsetTest {
    public static void main(String[] args) {
        float max = 1.0f;
        System.out.println("Checking " + max);
        String stringFloatFormatHex = String.format("%08x", Float.floatToRawIntBits(max));
        System.out.println(stringFloatFormatHex);
        byte[] bytesForFile = javax.xml.bind.DatatypeConverter.parseHexBinary(stringFloatFormatHex);
        String stringForFile = new String(bytesForFile);
        System.out.println(stringForFile);

        String charset = "windows-1252";
        try {
            Writer output = Files.newBufferedWriter(Paths.get("test.txt"), Charset.forName(charset));
            output.write(stringForFile);
            output.close();
        } catch (IOException e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
    }
}

控制台输出:

Checking 1.0
3f800000
?�  
Input length = 1
java.nio.charset.UnmappableCharacterException: Input length = 1
    at java.nio.charset.CoderResult.throwException(CoderResult.java:282)
    at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:285)
    at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
    at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
    at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
    at java.io.BufferedWriter.close(BufferedWriter.java:265)
    at CharsetTest.main(CharsetTest.java:21)

【问题讨论】:

  • 查看String(byte[]) 构造函数。不确定您的默认字符集是什么,但可能发生的情况是:第二个字符无法映射,因此将变成�,即 unicode 替换字符,在 Windows-1252 中无法编码。
  • 使用System.out.println("Default Charset=" + Charset.defaultCharset());,看起来我的默认字符集是UTF-8。我使用的是 Windows 10。
  • \0x80 不是有效的 utf-8 序列,因此被替换为 �.
  • 哦,我明白了!你是对的;问题出在指令String stringForFile = new String(bytesForFile); 上,位于DatatypeConverter 下方。当我在不提供字符集的情况下构造字符串时,它默认为 UTF-8,这是行不通的。但是,它只会在写入文件时抛出异常。
  • 是的,因为无法在 windows-1252 中编码,所以会抛出异常。发生在无法以某些字符集编码的所有字符上,这是一件好事。唯一的问题是 String 构造函数默默地替换无效序列是否是一件好事。但现在已经不可能改变了。

标签: java character-encoding windows-1252


【解决方案1】:

编辑: 问题出在指令String stringForFile = new String(bytesForFile); 上,位于DatatypeConverter 下方。当我在不提供字符集的情况下构造字符串时,它使用我的默认字符集,即 UTF-8,它没有代码点 80 的符号。但是,它仅在写入文件时引发异常。这在下面的代码中不会发生,因为我的重构(请记住 Johannes Kuhn 在 cmets 中的建议)在未指定字符集的情况下不使用 String(byte[]) 构造函数。

Johannes Kuhn's 关于String(byte[]) 构造函数的建议给了我一些很好的线索。我最终得到了以下代码,它看起来工作正常:甚至将 符号打印到控制台以及将其写入 test.txt。这表明代码点 80 可以使用 windows-1252 代码页进行翻译。

如果我在这一点上猜测为什么这段代码有效而另一个无效,我仍然会感到困惑,但我猜这是javax.xml.bind.DatatypeConverter.parseHexBinary(stringFloatFormatHex); 中的转换。这似乎是主要区别,尽管我不确定它为什么重要。

无论如何,下面的代码可以工作(我什至不必将其转换为字符串;我可以使用 FileOutputStream fos = new FileOutputStream("test.txt"); fos.write(bytes); fos.close(); 将字节写入文件),所以我对这个很满意。

代码:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;

public class BytesCharsetTest {
    public static void main(String[] args) {
        float max = 1.0f;
        System.out.println("Checking " + max);
        int convInt = Float.floatToRawIntBits(max);
        byte[] bytes = ByteBuffer.allocate(4).putInt(convInt).array();

        String charset = "windows-1252";
        try {
            String stringForFile = new String(bytes, Charset.forName(charset));
            System.out.println(stringForFile);

            Writer output = Files.newBufferedWriter(Paths.get("test.txt"), Charset.forName(charset));
            output.write(stringForFile);
            output.close();
        } catch (IOException e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
    }
}

控制台输出:

Checking 1.0
?€  

Process finished with exit code 0

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-16
    • 1970-01-01
    • 1970-01-01
    • 2017-10-19
    • 1970-01-01
    • 2012-09-08
    • 2011-04-27
    相关资源
    最近更新 更多