在 Java 中,使用从 InputStream.read() 返回的 int 调用 Character.isXxx() 方法是否安全?

In Java, is it safe to call Character.isXxx() methods with an int returned from InputStream.read()?

读取文本文件时,我想做这样的事情:

InputStream input = ...;
int read = input.read();
if (Character.isWhitespace(read)) {
    // do something with the whitespace
}

另一种方法是检查负 read() return 值(又名,输入结束)并显式转换:

InputStream input = ...;
int read = input.read();
if (read >= 0 && Character.isWhitespace((char) read)) {
    // do something with the whitespace
}

但是,这涉及到额外的分支和转换,我希望我的代码尽可能高效,所以我更喜欢第一种方法。

但是,我希望我的代码更健壮 :),我不确定第一种方法是否会产生细微的问题。根据我收集到的信息,Unicode 将 0xFFFF0xFFFFFFFF 都定义为非字符,所以我认为它是安全的。但专家怎么说?

为了确保,问题涉及我的方法是否对 所有 Character.isXxx() 方法安全,而不仅仅是 Character.isWhitespace().

是的,很安全。对于 0xFFFFFFFF 的情况,所有 isXxx 方法 return 都是假的。实际上,对于 0x000FFFFF 以外的所有内容都是如此,因为这些值在 Unicode 中未定义。对于 0xFFFF,它大部分是相同的,尽管 isBmpCodePoint 是正确的。

InputStream.read() 方法读取一个 单个 8 位字节 并且 returns 它作为 32 位 int 范围内的0x00 - 0xFF,或 returns -1 EOF。

接受 32 位 int 作为输入的 Character.isXXX() 方法需要 0x00 - 0x10FFFF 范围内的 完整 Unicode 代码点 。如果文件由 7 位 ASCII 字符组成(其中字节 0x00 - 0x7F 映射到代码点 U+0000 - U+007F),则单个字节 可能 按原样表示完整代码点,或 ISO-8859-1(其中字节 0x00 - 0xFF 映射到代码点 U+0000 - U+00FF)。如果文件使用 any 其他编码,则无法保证任何给定字节将按原样映射到具有相同值的代码点,尤其是如果字节大于 0x7F(大多数 7/8 位编码使用相同的字节值来实现 ASCII 兼容性 - 但并非全部如此!)。

接受 16 位 char 作为输入的 Character.isXXX() 方法需要 0x00 - 0xFFFF 范围内的 UTF-16 代码单元 .单个 char 可以按原样保留一个 Unicode 代码点,直到代码点 U+FFFF。但是,这些方法 支持 UTF-16 代理项,因此无法处理 U+FFFF 以上的 Unicode 代码点(需要 2 char 值来表示它们)。

那么,为了回答您的问题 - 您能否 read() 文件中的任何给定字节并将其按原样传递给 Character.isXXX() 方法并获得可靠的结果?答案是——这取决于文件的实际编码。如果文件以 7 位 ASCII 或 8 位 ISO-8859-1 编码,则可以。否则,可能,但通常仅适用于字节 0x7F,因为字节 0x80 - 0xFF 是特定于编码的,并且将取决于特定编码如何在字节和 Unicode 代码点之间映射(假设文件甚至开始使用 7/8 位编码)。