Java中的字节流与字符流

4

我正在研究Java中的I/O类。我知道有两种重要的流类型:字节流和字符流。但是...我已经尝试使用字节流读写文本文件,并且它可以正常工作。以下是代码:

    File klasor = new File("C:\\Java");
    if(!klasor.exists()) klasor.mkdirs();

    File kaynakDosya = new File("C:\\Java\\kaynak.txt");
    if(!kaynakDosya.exists()) kaynakDosya.createNewFile();

    File hedefDosya = new File("C:\\Java\\hedef.txt");
    if(!hedefDosya.exists()) hedefDosya.createNewFile();

    FileInputStream kaynak = new FileInputStream(kaynakDosya);
    FileOutputStream hedef = new FileOutputStream(hedefDosya);

    int c;
    while((c = kaynak.read()) != -1) {
        hedef.write(c);
    }

    if(kaynak != null) {
        kaynak.close();
    }

    if(hedef != null) {
        hedef.close();
    }

然后我用字符流做了同样的操作:
    File klasor = new File("C:\\Java");
    if(!klasor.exists()) klasor.mkdirs();

    File kaynakDosya = new File("C:\\Java\\kaynak.txt");
    if(!kaynakDosya.exists()) kaynakDosya.createNewFile();

    File hedefDosya = new File("C:\\Java\\hedef.txt");
    if(!hedefDosya.exists()) hedefDosya.createNewFile();

    FileReader kaynak = new FileReader(kaynakDosya);
    FileWriter hedef = new FileWriter(hedefDosya);

    int c;
    while((c = kaynak.read()) != -1) {
        hedef.write(c);
    }

    if(kaynak != null) {
        kaynak.close();
    }

    if(hedef != null) {
        hedef.close();
    }

这两种方法产生了相同的结果。因此,我想知道为什么我不应该在这里使用字节流而是使用字符流? (我已经阅读了一些文章以及相关问题的回答,它们都这样说) 我知道字符流会逐个字符地读取它,但这给我带来了什么优势?或者如果我使用字节流读取字符会出现什么问题?我希望我的问题很明确。我会感激真实的案例。

2个回答

3
将字符写入字节定向输出流(或从字节定向输入流读取字符)只有在流中的所有字符都可以由平台的默认编码(通常为UTF-8,但也可能是其他编码)表示为单个字节时,才会产生与使用字符定向流相同的结果。要测试这一点,请尝试包含需要超过一个字节来表示的内容的文件(例如希腊语、西里尔文或阿拉伯语字符)。使用字节定向流将无法正常工作。而使用字符定向流,只要两个流都使用支持这些字符的编码(如UTF-8),并且输入文件以用于输入流的编码存储,则字符将被保留。
请注意,您的字节定向代码实际上并没有测试这一点,因为它只是逐字节复制文件。一切看起来都像是正常工作的,但是如果您尝试读取实际字符(比如说,将其与代码中的实际文本进行比较),则将失败。要测试这一点,请创建一个文件(例如UTF-8编码)包含西里尔文文本“Привет!”。然后在代码中,尝试使用字节定向输入流将该文本读取到一个String中,并测试它是否实际包含了您期望的内容。
System.out.println("Success: " + "Привет!".equals(input));

1
我有点困惑:不是所有字符都由两个字节表示吗?你说的“如果文件中的所有字符都可以用单个字节表示”是什么意思?我已经尝试了使用西里尔文“Привет!”也可以工作!此外,我保持源文件的编码为“UTF-8”,将目标文件的编码更改为“ANSI”,它仍然可以工作。 - user8177292
1
@AdemTepe - 在UTF-8中,代码点高达0x7F由单个字节表示。(例如,请参见此线程。)您的面向字节的代码可以很好地按字节复制文件,但这并没有解决如果您尝试将这些字节解释为字符(在输入时)或者如果您尝试将字符写入面向字节的流会发生什么。我将更新我的答案以澄清这一点。 - Ted Hopp

2

java.io.FileInputStream的javadoc说明:

FileInputStream用于读取原始字节流,例如图像数据。如果要读取字符流,请考虑使用FileReader。

java.io.FileOutputStream的javadoc也有类似的说明:

FileOutputStream用于写入原始字节流,例如图像数据。如果要写入字符流,请考虑使用FileWriter。

FileInputStream/FileOutputStreamFileReader/FileWriter之间的主要区别之一是前者提供操作字节的方法,而后者提供操作字符的方法。

在您的示例中,将文件内容复制到另一个文件中时,操纵char或byte并没有太大的区别。
在您的情况下,FileInputStreamBufferedInputStream似乎更为合适。

但是,如果您使用流来从/到String实例中读取/写入字符, 使用FileReader/FileWriter可以使事情更加清晰简单。
此外,您还可以将FileReader/FileWriter包装到BufferedReader/BufferedWriter中,并从字符、数组和行的高效读取/写入中受益。

 BufferedWriter writer = new BufferedWriter(new FileWriter("myfile"));
 writer.append(oneString);
 writer.append(oneStringBuffer);
 writer.newLine();

 BufferedReader reader = new BufferedReader(new FileReader("myfile"));
 String currentLine = reader.readLine();

我们应该将 PDF 作为字节流还是字符流读取?谢谢。 - Diego Ramos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接