理解Java中的二进制、字节流和字符

4
我在理解Java IO类的一些概念时遇到了一些困难。例如有两种类型的流,字节流和字符流。据我所知,字节流逐字节读取。

1.如果Java中的字符作为16位(两个字节)数据类型存储,那么如何通过面向字节的输入流(例如FileInputStream)准确地读取一个字符,比如“ A”?

2.是否是我使用的字符(大多数在ascii图表上介于0和122之间)存储在分配的两个字节中的一个字节中?

3.DataInputStream / DataOutputStream允许我读写二进制数据,其他输入流(如FileInputStream / FileOutputStream)允许我读写什么内容?基本上,我想知道在我希望将数据输出为可读的文本(使用像记事本这样的简单文本编辑器)时使用哪个流,相对于当我想要将其编码为原始二进制数据(在记事本中看起来像垃圾文本)时使用哪个流?

很难理解Java中的流的概念以及何时使用哪个流。


字符流处理的是字符,而不是字节。说字符流“逐字节读取”并不准确。 - davmac
2
char 是一个16位数据类型。它不存储字符,而是存储UTF-16代码单元。一个或两个UTF-16代码单元组成一个UTF-16代码点。代码点标识特定的Unicode字符。此外,您正在查看错误的字符集。Java通常使用Unicode,尽管一些流类默认为操作系统默认字符集。 - Tom Blodget
3个回答

4

根据您正在阅读的文件格式而定。

如果文件是一串ASCII字节流,则执行以下操作:

InputStream is = new FileInputStream( filePath );
Reader reader = new InputStreamReader( is, "ISO-8859-1" );

char ch = reader.read();

您首先需要在字节定向文件上打开输入流。然后,InputStreamReader会将字节转换为字符。当然,在这种情况下,ISO-8859-1是从单字节值到完全相同的字符值的映射。显然,其他映射也是可能的,但ISO-8859-1恰好与Unicode集的前255个字符相同,其中前127个字符恰好与ASCII相同。

在写入时,请使用:

OutputStream os = new FileOutputStream( filePath ) ;
Writer w = new OutputStreamWriter( os, "ISO-8859-1" );

w.write( ch );

再次强调,OutputStreamWriter会根据ISO-8859-1字符集适当地在字符和字节流之间进行转换。生成的文件每个字符将有一个字节。

以下是一些正确的基本流模式示例。(参考链接)

如果你使用以上内容执行以下操作:

w.write("AAAA");
w.flush();
w.close();

生成的文件将包含4个字节,每个字节的值都为65。使用顶部的代码将该文件读回内存后,内存中会有四个"A"字符,但是在内存中,每个char占用16位。
如果文件以不同的字符集编码,包括可能是多字节字符集,则只需在InputStreamReader/OutputStreamWriter中使用正确的编码方式,即可在读取和写入时进行适当的转换。
UTF-8不是一个字符集,而是普通Unicode字符的一种字节序列编码方式。UTF-8编码非常巧妙,因为Unicode字符的前127个字符被映射到了前127个字节值(作为单独的字节)。然后,字符> = 128在连续的2个或更多个字节值中使用,其中每个字节值> = 128。如果您知道ASCII文件仅使用“7位” ASCII,则UTF-8也适用于您。对于Java来说,UTF-8是最好的编码方式,因为它可以正确编码所有可能的Java char值而不会丢失数据。
了解流的这些内容非常重要。我建议您不要尝试以任何其他方式将字节转换为字符,因为流中的转换非常可靠和正确。
(更糟糕的是...实际上,Character是一个32位的变量,其中20位可以用一种称为UTF-16的编码方式编码成16位char值的序列。建议您暂时忽略这一点,但要知道,即使在由16位char值组成的Java字符串中,也存在一些双字符序列。)

回答不错,但是......Java中ASCII的Charset是"US-ASCII"而不是"ISO-8859-1"。我猜你提到"ISO-8859-1"是因为很难找到还在使用ASCII的例子。 - Tom Blodget
我理解你所说的。有时候,由于读取文件的选项太多,可能会有些混淆。有些东西我还不是很明白,但我想根据你的回答和其他答案再多读一些资料,然后尝试编写一些代码,看看能得出什么结果。非常感谢! - jmreader
@Tom 从技术上讲,你是正确的,但那个答案并不是很有用。应该基本上总是使用ISO-8859-1而不是US-ASCII。 US-ASCII仅定义了128个字符。一个字节可以包含256个值。如何处理其他值? ISO-8859-1的前128个字符与US-ASCII完全相同。如果文件只包含7位ASCII,则两种编码都能很好地工作;US-ASCII没有优势。但是,如果出现字节值> 127,则ISO-8859-1提供了合理的处理方式,并且ISO-8859-1是WWW上的默认编码,因此许多文件都以这种方式编码。 - AgilePro

1
如果在Java中,char类型被存储为16位(两个字节)数据类型,那么我如何能够通过面向字节的输入流(例如FileInputStream)准确地从文件中读取一个字符,比如'A'呢?
请尝试进行以下操作:
System.out.println(Integer.toBinaryString('A'));

该程序输出字符'A'的二进制表示。这将打印出:

1000001

由于'A'是一个char类型,它实际上是用16位存储的。

00000000 01000001

所以,您需要做的就是读取两个连续的字节,并适当地使用它们来形成一个char。看看它在操作中是如何实现的。

ByteBuffer buffer = ByteBuffer.wrap(new byte[] {0b00000000, 0b01000001});
System.out.println(buffer.getChar());

打印

A

这段代码的作用是使用数组中的第一个byte作为char的前8位,第二个byte作为后8位。
让我读写二进制数据,而像这样的其他输入流则允许我读写什么?我想知道在我希望将数据输出为可读的文本(使用像记事本这样的简单文本编辑器)时应该使用哪个流,而在我希望将其编码为原始二进制数据(在记事本中看起来像垃圾文本)时应该使用哪个流?
无论您是编写文本还是其他任何内容,它都是位和字节。您完全可以使用
"someString".getBytes()

并将它们写入。所以实际上并不重要。使用最能代表你正在做的内容。通常,您可以使用PrintWriter包装基础的OutputStream,并使用ScannerBufferedReader包装基础的InputStream


我喜欢你的回答,这很棒 - ByteBuffer buffer = ByteBuffer.wrap(new byte[] {0b00000000, 0b01000001}); System.out.println(buffer.getChar()); - Farhan stands with Palestine

1
在我回答你的问题之前,有几件非常基本的事情需要理解。
  1. 在最低级别的流(InputStream/OutputStream)中,一切都是比特和字节。因此,最低级别的流处理的是比特/字节的原始数据。
  2. 现在,要将原始字节转换为可读字符,您需要使用字符编码或字符集。简而言之,字符编码是将原始字节转换为来自已定义集合(例如UTF-8)的可读字符的指令(从字节到可视字符的映射)。
现在回到你的问题:
如果Java中的字符存储为16位(两个字节)数据类型,那么如何通过面向字节的输入流(例如FileInputStream)准确地读取一个字符,比如'A'?
为了读取字符数据,原始输入流被包装在字符定向流中,例如:
FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader isr = new InputStreamReader(fis, "UTF8"); 

正如javadoc所说,InputStreamReader是从字节流到字符流的桥梁。

我的字符(大多在0和122之间的ascii图表上)是否存储在分配的两个字节中的一个字节中?

是的。ASCII字符集是较大的Unicode集的子集,如UTF-8

DataInputStream/DataOutputStream允许我读写二进制数据,而FileInputStream/FileOutputStream允许我读写什么内容?

我想现在很明显了,DataInputStream/DataOutputStream用于字符数据,而ileInputStream/FileOutputStream用于原始数据。

我基本上想知道当我希望输出数据作为文本以便我可以阅读(使用像记事本这样的简单文本编辑器)时使用哪个流,而当我希望将其编码为原始二进制数据时使用哪个流(在记事本中看起来像垃圾的文本)?

使用任何读写器来处理文本(这里是一个示例


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