Java Unicode 混淆

6

大家好,我刚开始学习Java,遇到了一些非常困惑的问题!

我正在打一个示例,来展示char数据类型。这是我使用的书中的例子。

代码如下:

public class CharDemo
{
public static void main(String [] args)
{
char a = 'A';
char b = (char) (a + 1);
System.out.println(a + b);
System.out.println("a + b is " + a + b);
int x = 75;
char y = (char) x;
char half = '\u00AB';
System.out.println("y is " + y + " and half is " + half);
}
}

让我感到困惑的是语句char half = '\u00AB'。书中指出,\u00AB是符号“1/2”的代码。如描述的那样,当我从cmd编译和运行程序时,在这一行产生的符号实际上是“1/2”。
所以一切似乎都按照预期工作。我决定尝试一些不同的Unicode并玩弄代码。我谷歌了多个Unicode表格,但没有一个与上述结果一致。
在我找到的每一个表格中,都说明了代码/u00AB不是“1/2”,而是这个: http://www.fileformat.info/info/unic...r/ab/index.htm 那么Java使用哪种字符集呢?我认为UNicode应该只有一个,但我已经搜寻了几个小时,没有地方说明/u00AB等于1/2,然而这正是我的java编译器将其解释为的内容。
我肯定错过了某些明显的东西!感谢任何帮助!

3
我推荐这篇文章帮助你理解你面临的问题:关于Unicode和字符集,每个软件开发人员绝对、肯定必须知道的最低限度 - Adam Paynter
5个回答

16

在Windows平台上,控制台编码不匹配是一个众所周知的问题。

Java Runtime期望系统控制台使用的编码与系统默认编码相同。然而,Windows使用两个分离的编码:ANSI编码页(系统默认编码)和OEM编码页(控制台编码)

因此,当你尝试将Unicode字符U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK写入控制台时,Java Runtime期望控制台编码是ANSI编码(例如Windows-1252),其中该Unicode字符表示为0xAB。然而,实际的控制台编码是OEM编码(例如CP437),在这种情况下,0xAB表示½

因此,使用 System.out.println() 将数据打印到Windows控制台会产生错误的结果。

为了获得正确的结果,你可以使用 System.console().writer().println() 代替。


谢谢,这很合理,但您提到将数据打印到Windows控制台会产生错误的结果。这个例子直接来自一本Java书籍,作者知道AB将是一半。这只是他没有解释清楚的糟糕写作吗? - Nick
1
@Nick:那么这就是一篇糟糕的文章。也许作者很少使用非美国ASCII字符,因此对这个问题不熟悉。 - axtavt
1
+1。这真的很傻。Java和Windows都使用本地Unicode字符串,以UTF-16LE编码存储在内存中。然而,它们仍然无法在不通过字符混淆的编码解码循环的情况下相互通信。 - bobince

4
\u00ab字符并不是1/2字符;请参考Unicode.org网站上的这个权威代码页
我认为你看到的是在一个默认字符编码不是UTF-8或Latin-1的平台上使用System.outPrintStream所导致的。也许像@axtavt的回答所建议的那样,它是一些Windows字符集?(这也有可能解释为什么\u00ab被显示为1/2而不是某个“splat”字符。)
(在Unicode和Latin-1中,\00BD1/2字符的代码点。)

3

0xAB在古老的Codepage 437中代表1/2,这是Windows终端默认使用的编码方式,无论您实际设置了哪种编码方式

因此,事实上,该字符值对于Java程序来说表示“«”字符,如果您在GUI中呈现该字符或在正常操作系统上运行它,则会得到该字符。如果您想在Windows中看到正确的输出,请将CMD中的字体设置切换为非“Raster Fonts”(单击左上角图标,属性,字体选项卡)。例如,使用Lucida Console,我可以这样做:

C:\Users\Documents>java CharDemo
131
a + b is AB
y is K and half is ½    

C:\Users\Documents>chcp 1252
Active code page: 1252

C:\Users\Documents>java CharDemo
131
a + b is AB
y is K and half is «

C:\Users\Documents>chcp 437
Active code page: 437

2
哦,如果你有机会见到你书中的作者,一定用它狠狠地敲打他/她的头部。 - themel
有没有其他推荐的书籍?但是必须从非常基础的地方开始,包括基本原理和基础知识。 - Nick

2

Java的一个优点是它基于Unicode。这意味着,你不仅可以在数据字符串中使用非英文字母的书写系统(例如中文或数学符号),还可以在函数和变量名称中使用。

下面是一个使用Unicode字符作为类名和变量名的示例代码。

class 方 {
    String 北 = "north";
    double π = 3.14159;
}

class UnicodeTest {
    public static void main(String[] arg) {
        方 x1 = new 方();
        System.out.println( x1.北 );
        System.out.println( x1.π );
    }
}

Java是在Unicode标准定义了一个更小的字符集时创建的。当时认为16位足以编码所需的所有字符。因此,Java被设计为使用UTF-16。实际上,char数据类型最初被用来表示16位Unicode代码点。

UTF-8字符集由RFC 2279指定;

UTF-16字符集由RFC 2781指定

UTF-16字符集使用16位数量,因此对字节顺序敏感。在这些编码中,流的字节顺序可以通过表示Unicode字符'\uFEFF'的初始字节顺序标记来指示。字节顺序标记的处理如下:

When decoding, the UTF-16BE and UTF-16LE charsets ignore byte-order marks; when encoding, they do not write byte-order marks.

When decoding, the UTF-16 charset interprets a byte-order mark to indicate the byte order of the stream but defaults to big-endian if there is no byte-order mark; when encoding, it uses big-endian byte order and writes a big-endian byte-order mark.

此外,也可以查看这个链接


2
UTF-8和UTF-16不是字符集,它们是同一字符集Unicode的两种不同的可变宽度编码。 - tchrist

0

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