Java 中的 FileInputStream 和 Unicode

FileInputStream and Unicode in Java

我是新手Java,我尝试了解字节流和字符流,我看到很多人说字节流只适用于ASCII字符集,而字符流可以支持所有类型字符集ASCII,Unicode等。我认为有一个误解,因为我可以使用byte strem来读写一个Unicode字符。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class DemoApp {

    public static void main(String args[]) {

        FileInputStream fis = null;
        FileOutputStream fos = null;

        try {

            fis = new FileInputStream("abc.txt");
            fos = new FileOutputStream("def.txt");
            int k;

            while ((k = fis.read()) != -1) {

                fos.write(k);
                System.out.print((char) k);
            }
        }

        catch (FileNotFoundException fnfe) {

            System.out.printf("ERROR: %s", fnfe);
        }

        catch (IOException ioe) {

            System.out.printf("ERROR: %s", ioe);
        }

        finally {

            try {

                if (fos != null)
                    fos.close();
            }

            catch (IOException ioe) {

                System.out.printf("ERROR: %s", ioe);
            }

            try {

                if (fis != null) 
                    fis.close();
            }

            catch (IOException ioe) {

                System.out.printf("ERROR: %s", ioe);
            }

        }

    }

}

abc.txt 文件包含 Unicode 字符 Ǽ,我使用 UTF-8 编码保存文件。代码运行良好,它创建了一个新文件 def.txt 并且该文件包含 Unicode 字符 Ǽ.

我有两个问题:

  1. 字节流关于Unicode字符的真相是什么?字节流是否支持Unicode字符?

  2. 当我尝试使用 s.o.p((char) k) 打印时,结果不是 Unicode 字符,它只是 ASCII 字符:Ǽ。而且我不明白为什么结果不是 Unicode 字符,因为我知道 Java 和 char 数据类型支持 Unicode 字符。我尝试将此代码保存为 UTF-8,但问题仍然存在。

对不起我的英语语法,提前谢谢你!

What is the truth about byte stream regarding Unicode character? Does byte stream support Unicode character or not?

事实上,没有 "Unicode character" 这样的东西。您不应混淆三个不同的概念。

  • Unicode 代码点
  • 代码点序列的某种编码中的字符。
  • Java char 类型,两者都不是。严格来说。

你需要认真阅读一下背景资料:

弄清这一点后,我们可以说虽然字节流可用于读取 Unicode 代码点 [=57] 序列的 编码 =],流 API 设计不是为了读写任何形式的基于字符的文本而设计的。它是为读取和写入字节序列(8 位二进制值)而设计的……它可以代表任何东西。 Stream API 被设计成不知道字节代表什么:它不知道,也不关心!

When I try to print with s.o.p((char) k) the result is not an Unicode character, it is just ASCII character: Ǽ. And I don't understand why the result is not an Unicode character because I know that Java and char data type support Unicode character. I tried to save this code as UTF-8 but the problem persists.

(更正。这些不是 ASCII 字符,它们是 LATIN-1 字符!)

问题不在Java。问题是控制台被配置为期望文本以特定的字符编码发送给它,但您发送的字符使用不同的编码。

当您使用流读取和写入字符时,流不知道也不关心编码。因此,如果您读取一个有效的 UTF-8 编码文本文件并使用流将其写入期望(例如)LATIN-1 的控制台,那么结果通常是垃圾。

另一种获取垃圾的方法(这就是这里发生的事情)是将编码文件读取为字节序列,然后将字节转换为字符并打印字符。那是错误的做法。如果要字符正确输出,需要将字节解码成字符序列,然后打印字符。转换不是解码。

如果您通过 Reader 读取字节,解码会自动进行,您不会得到那种重整。 (您可能会得到另一种......如果控制台无法显示字符,或者如果您配置 Reader 堆栈以使用错误的字符集解码。)


总结:如果您正在尝试制作文件的文字副本(例如),请使用字节流。如果您尝试将文件作为文本处理,请使用字符流。

您的示例代码的问题在于,您似乎试图通过一次遍历文件同时执行这两项操作;即制作文件的文字副本并将其显示为控制台上的文本。这在技术上是可行的……但很难。我的建议:不要试图同时做这两件事。