【问题标题】:In Java: why some Stream methods take int instead of byte or even char?在 Java 中:为什么某些 Stream 方法采用 int 而不是 byte 甚至 char?
【发布时间】:2010-06-30 19:33:39
【问题描述】:

为什么某些将bytes/chars 写入流的方法采用int 而不是byte/char??

如果是int 而不是char,有人告诉我: 因为 char 在 java 中只有 2 个字节的长度,这对于已经在使用的大多数字符符号来说是可以的,但是对于某些字符符号(中文或其他),字符被表示为超过 2 个字节,因此我们使用 int而是。

这个解释离事实有多近?

编辑: 我用stream这个词来表示二进制和字符流(不仅仅是二进制流)

谢谢。

【问题讨论】:

  • 您应该具体说明这一点。特别是,流通常没有与char 相关的方法,因为它们用于二进制 数据; text 数据需要适当的编码,而流本身不应该知道。
  • 普通流只接受二进制数据,即字节,而不是字符或整数。如果您想发送文本,我建议您使用具有所选编码的 Writer,对于 char、short、int 等,您可以使用像 DataOutputStream 这样的 adpater。
  • 谢谢,但我不是在问如何将字符串写入文件!
  • “使用流词来表示二进制和字符流(不仅仅是二进制流)”那么您使用的词是错误。您应该学会使用正确的语义,其他任何东西都只是浪费混乱。流仅用于字节,其他接口(写入器/读取器)处理字符和其他更高级别的抽象。

标签: java character-encoding streaming iostream


【解决方案1】:

有人告诉我,如果是 int 而不是 char:因为 java 中的 char 只有 2 个字节长度,这对于已经使用的大多数字符符号来说是可以的,但是对于某些字符符号(中文或其他),该字符正在以超过 2 个字节表示,因此我们使用 int 代替。

假设此时您正在专门讨论Reader.read() 方法,那么您所讲述的“某人”的陈述实际上是不正确

确实,某些 Unicode 代码点的值大于 65535,因此不能表示为单个 Java char。但是,Reader API 实际上生成了 Java 的char 值序列(或 -1),而不是 Unicode 代码点序列。这在javadoc 中明确说明。

如果您的输入包含大于 65535 的(经过适当编码的)Unicode 代码点,那么您实际上需要调用 read() 方法两次才能看到它。您将得到一个 UTF-16 代理对;即两个 Java char 值,一起 表示代码点。事实上,这符合 Java String、StringBuilder 和 StringBuffer 类的所有工作方式;它们都使用基于 UTF-16 的表示形式……带有嵌入的代理对。

Reader.read() 返回 int 而不是 char 的真正原因是允许它返回 -1 以表示没有更多字符要读取。同样的逻辑解释了为什么InputStream.read() 返回int 而不是byte

假设,我认为 Java 设计者可以指定read() 方法抛出异常以表示“流结束”条件。但是,这只会将一个潜在的错误来源(未能测试结果)替换为另一种(未能处理异常)。此外,异常相对昂贵,流结束并不是真正的意外/异常事件。简而言之,目前的方法更好,IMO。

Reader API 的 16 位特性的另一个线索是 read(char[], ...) 方法的签名。如果不使用代理对,它将如何处理大于 65535 的代码点?)

编辑

DataOutputStream.writeChar(int) 的情况确实有点奇怪。但是,javadoc 明确指出该参数被写入为 2 字节值。事实上,该实现显然只将底部的两个字节写入底层流。

我认为这没有充分的理由。无论如何,有一个错误数据库条目(4957024),标记为“11-Closed,Not a Defect”,并带有以下注释:

“这不是一个很好的设计或借口,但它太老套了,我们无法改变。”

...这是一种承认它是一个缺陷,至少从设计的角度来看。

但这不值得大惊小怪,IMO。

【讨论】:

  • 不要忘记,Java 的设计者和最初的目标受众都习惯了 C,因此他们尽可能地使 read() 与 fgetc() 相似。跨度>
  • 嗯...我不同意。如果他们想让它变得非常相似,他们会调用方法getC 或其他东西。我敢肯定,C 库通知了设计师,但有很多迹象表明他们没有打算模仿他们.
  • 您对read 方法的返回类型的解释非常贴切。那么,您能否解释一下为什么 DataOutputStream#writeChar 方法需要一个整数? java.sun.com/javase/6/docs/api/java/io/…
  • 不要忘记 read() 必须返回带外值以及带内值。这是真实的解释。尽管你对 fgetc 有猜测。
  • 我回答了你关于 DataOutputStream#writeChar 的问题;看到编辑,还有什么你期待的吗?
【解决方案2】:

我不确定你指的是什么,但也许你在想InputStream.read()?它返回一个整数而不是一个字节,因为返回值被重载以表示流的结尾,表示为 -1。由于有 257 个不同的可能返回值,因此一个字节是不够的。

否则也许你可以提供一些更具体的例子。

【讨论】:

    【解决方案3】:

    有几种可能的解释。

    首先,正如一些人所指出的,这可能是因为 read() 必然返回一个 int,因此让 write() 接受一个 int 以避免强制转换可以被视为优雅:

    int read = in.read();
    if ( read != -1 )
       out.write(read);
    //vs
       out.write((byte)read);
    

    其次,最好避免其他类型的强制转换:

    //write a char (big-endian)
    char c;
    out.write(c >> 8);
    out.write(c);
    
    //vs
    out.write( (byte)(c >> 8) );
    out.write( (byte)c );
    

    【讨论】:

      【解决方案4】:

      最大可能的代码点是 0x10FFFF 是正确的,它不适合 char。但是,流方法是面向字节的,而写入器方法是 16 位的。 OutputStream.write(int) 写入单个字节,Writer.write(int) 只查看低位 16 位。

      【讨论】:

        【解决方案5】:

        在 Java 中,流用于原始字节。要编写字符,您将 Stream 包装在 Writer 中。

        虽然Writers 确实有write(int)(它写入 16 个低位;它是一个 int,因为字节太小,而 short 由于它被签名而太小),你应该使用 write(char[])write(String) 代替。

        【讨论】:

          【解决方案6】:

          可能与返回 int 的 read() 方法对称。没什么大不了的。

          【讨论】:

            猜你喜欢
            • 2011-08-20
            • 2014-04-01
            • 2013-07-01
            • 2015-01-23
            • 1970-01-01
            • 2010-10-21
            • 1970-01-01
            • 2016-02-25
            相关资源
            最近更新 更多