Java:读者和编码

时间:2009-12-11 13:50:21

标签: java encoding io

Java的默认编码为ASCII。是? (见下面的编辑)

UTF-8中编码文本文件时?读者如何知道他必须使用UTF-8

我所谈到的读者是:

  • FileReaderŠ
  • BufferedReader来自Socket s
  • 来自Scanner
  • System.in
  • ...

修改

我们的编码取决于操作系统,这意味着每个操作系统都不会出现以下情况:

'a'== 97

5 个答案:

答案 0 :(得分:22)

  

读者如何知道他必须使用UTF-8?

您通常会在InputStreamReader中指定您自己。它有一个构造函数采用字符编码。 E.g。

Reader reader = new InputStreamReader(new FileInputStream("c:/foo.txt"), "UTF-8");

所有其他读者(据我所知)使用平台默认字符编码,这可能确实不是正确的编码(例如 -cough - CP-1252

理论上,您还可以根据byte order mark自动检测字符编码。这将几种unicode编码与其他编码区分开来。遗憾的是,Java SE没有任何API,但您可以自制一个可用于替换InputStreamReader的API,如上例所示:

public class UnicodeReader extends Reader {
    private static final int BOM_SIZE = 4;
    private final InputStreamReader reader;

    /**
     * Construct UnicodeReader
     * @param in Input stream.
     * @param defaultEncoding Default encoding to be used if BOM is not found,
     * or <code>null</code> to use system default encoding.
     * @throws IOException If an I/O error occurs.
     */
    public UnicodeReader(InputStream in, String defaultEncoding) throws IOException {
        byte bom[] = new byte[BOM_SIZE];
        String encoding;
        int unread;
        PushbackInputStream pushbackStream = new PushbackInputStream(in, BOM_SIZE);
        int n = pushbackStream.read(bom, 0, bom.length);

        // Read ahead four bytes and check for BOM marks.
        if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) {
            encoding = "UTF-8";
            unread = n - 3;
        } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) {
            encoding = "UTF-16BE";
            unread = n - 2;
        } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) {
            encoding = "UTF-16LE";
            unread = n - 2;
        } else if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) && (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) {
            encoding = "UTF-32BE";
            unread = n - 4;
        } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) {
            encoding = "UTF-32LE";
            unread = n - 4;
        } else {
            encoding = defaultEncoding;
            unread = n;
        }

        // Unread bytes if necessary and skip BOM marks.
        if (unread > 0) {
            pushbackStream.unread(bom, (n - unread), unread);
        } else if (unread < -1) {
            pushbackStream.unread(bom, 0, 0);
        }

        // Use given encoding.
        if (encoding == null) {
            reader = new InputStreamReader(pushbackStream);
        } else {
            reader = new InputStreamReader(pushbackStream, encoding);
        }
    }

    public String getEncoding() {
        return reader.getEncoding();
    }

    public int read(char[] cbuf, int off, int len) throws IOException {
        return reader.read(cbuf, off, len);
    }

    public void close() throws IOException {
        reader.close();
    }
}

编辑作为对您的修改的回复:

  

所以编码取决于操作系统。这意味着并非每个操作系统都是如此:

'a'== 97

不,这不是真的。 ASCII编码(包含128个字符,0x00直到0x7F)是所有其他字符编码的基础。只有ASCII字符集之外的字符可能会在另一种编码中以不同方式显示。 ISO-8859编码涵盖ASCII范围内具有相同代码点的字符。 Unicode编码涵盖ISO-8859-1范围内具有相同代码点的字符。

您可能会发现每个博客都是有趣的读物:

  1. The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)(更多理论上的两个)
  2. Unicode - How to get the characters right?(更实际的两个)

答案 1 :(得分:10)

Java的默认编码取决于您的操作系统。对于Windows,它通常是“windows-1252”,对于Unix,它通常是“ISO-8859-1”或“UTF-8”。

读者知道正确的编码,因为您告诉它正确的编码。不幸的是,并非所有读者都允许您这样做(例如,FileReader没有),所以通常您必须使用InputStreamReader

答案 2 :(得分:5)

对于大多数读者来说,Java使用任何编码和放大器。您的平台所做的字符集 - 这可能是ASCII或UTF-8的某种风格,或者像JIS(在日本)更具异国情调的东西。然后将此集中的字符转换为Java在内部使用的UTF-16。

如果平台编码与文件编码不同(我的问题 - UTF-8文件是标准的,但我的平台使用Windows-1252编码),有一种解决方法。创建一个使用指定编码的构造函数的InputStreamReader实例。

编辑:这样做:

InputStreamReader myReader = new InputStreamReader(new FileInputStream(myFile),"UTF-8");
//read data
myReader.close();

但是,IIRC有一些条款可以自动检测常见编码(例如UTF-8和UTF-16)。 UTF-16可以通过开头的字节顺序标记来检测。 UTF-8也遵循一定的规则,但通常你的平台编码和UTF-8的差异不重要,除非你使用国际字符代替拉丁文字符。

答案 3 :(得分:5)

我想先介绍这一部分:

  

Java的默认编码是ASCII。是

Java环境中至少有4种不同的东西可以称为“默认编码”:

  1. “默认字符集”是Java在运行时用于将字节转换为字符(以及byte[]String)的内容,当没有指定其他内容时。这个取决于平台,设置,命令行参数,......通常只是平台默认编码。
  2. Java在char值和String对象中使用的内部字符编码。这个总是 UTF-16!没有办法改变它,它只是UTF-16!这意味着代表char 始终a具有数值97,而代表π的字符始终具有数值960。
  3. Java用于在.class文件中存储字符串常量的字符编码。这个总是 UTF-8。没有办法改变它。
  4. Java编译器用于解释.java文件中的Java源代码的字符集。这个默认为默认字符集,但可以在编译时配置。
  5.   

    读者如何知道他必须使用UTF-8?

    没有。如果您有一些纯文本文件,那么必须知道编码才能正确读取它。如果你很幸运,你可以猜测(例如,你可以尝试平台默认编码),但这是一个容易出错的过程,在许多情况下你甚至没有办法意识到你猜错了。这是特定于Java。这对所有系统都是如此。

    某些格式(如XML和所有基于XML的格式)在设计时考虑到了这一限制,并包含了一种在数据中指定编码的方法,因此不再需要猜测。

    阅读The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)了解详情。

答案 4 :(得分:0)

您可以在java Charset API

开始了解这个想法

请注意,根据文档,

  

的原生字符编码   Java编程语言是UTF-16

编辑:

抱歉,在我完成这件事之前我被叫走了,也许我不应该发布部分答案。无论如何,其他答案解释了细节,重点是java将正确读取每个平台的本机文件字符集以及常见的备用字符集。