将unicode字符转换为int会给出错误的代码

时间:2019-05-24 09:21:57

标签: java string unicode char

我对Java还是很陌生,所以请保持柔和。

这似乎是一个常见的问题,但我似乎仍然找不到所需的答案。

我正在编写一个控制台应用程序,它将使用一串字符并将它们打印在屏幕上,但更大。例如:“ JAVA”将打印为:

 JJJJJ   A   V   V   A
   J    A A  V   V  A A
   J   A   A V   V A   A
   J   AAAAA V   V AAAAA
   J   A   A V   V A   A
 J J   A   A  V V  A   A
 JJJ   A   A   V   A   A

没什么特别的。字符串被分解为字符,然后在一个大的开关盒中查找每个字符,然后返回较大的字母。在必要时进行一些包装后,将大字母粘在一起并打印。

这太容易了,并且由于我想让生活变得更具挑战性,所以我想允许某些unicode字符,例如黑心(❤)\ u2674(无论如何,这就是Windows字符映射所声称的) )。基本上,将某种代码传递给参数将在内部替换为强字符并解释为unicode字符,例如:JAVA {HEART}可能会输出(我知道心脏被弄乱了,但是使用等宽字体可以正常显示):

 JJJJJ   A   V   V   A     ❤❤  ❤❤
   J    A A  V   V  A A   ❤❤❤❤❤❤
   J   A   A V   V A   A   ❤❤❤❤❤
   J   AAAAA V   V AAAAA    ❤❤❤❤
   J   A   A V   V A   A     ❤❤❤
 J J   A   A  V V  A   A      ❤❤
 JJJ   A   A   V   A   A       ❤

据我所知,unicode应该适合一个char(2个字节),并且绝对应该适合int(4个字节),所以我做了一个实验。在大街上的话是强制转换为int会给您字符代码。

String unicodeStr = "\u2674"; // Unicode for black heart.
System.out.println(unicodeStr.getBytes().length); // Only one byte, so should fit into a char, right?

char unicode = '\u2674'; // All good so far.
System.out.println((int)unicode); // Returns 9844. WTAF??

System.exit(-1); // Argh! Oh noez... Panic!

很明显,我在这里误解了一些东西,但是我不知道是什么。请有人可以解释为什么我输入了错误的字符代码吗?我曾尝试使用codePoints,但显然我也不知道我在做什么。如果有人能指出我正确的方向,我将永远感激不已。目的是将字符串分割成多个字符,然后通过开关盒将每个字符转换成一个大字母。

3 个答案:

答案 0 :(得分:2)

根据specificationgetBytes()使用平台默认值字符集对字符串进行编码,这与Java的内部编码UTF-16不同。这就是为什么您的getBytes()返回一个长字节数组的原因。

但是实际上,字符'\u2674'的UTF-16表示形式可以放入单个字符中,因为9844是十六进制值0x2674的十进制表示形式。

但是我仍然建议您使用codePoints,因为有些字符不能存储在单个字符中,例如U+1D161)。

要使用codePoints迭代String,可以使用以下代码:

public class Main {

    public static void main(String[] args) {
        String str = "JAVA\uD834\uDD61\u2665";
        int len = str.length();
        for(int i = 0; i < len; ) {
            int cp = str.codePointAt(i);
            i += cp > 0xFFFF ? 2 : 1;

            if(cp == "\u2665".codePointAt(0)) {
                System.out.println("Heart!");
            }
            else if(cp == "\uD834\uDD61".codePointAt(0)){
                System.out.println("Music!");
            }
            else{
                System.out.println((char)cp);
            }
        }
    }

}

输出:

JAVA♥
size: 6
J
A
V
A
Music!
Heart!

我们为什么要使用\uD834\uDD61来代表U+1D161

根据wikipedia,为了表示UTF-16中的U + 10000〜U + 10FFFF字符,我们需要用0x10000减去0x1D161,然后得到0x0D161,即(0000 1101 0001 0110 0001)以二进制格式。

然后,我们取高十位,即(0000 1101 00)或0x034,将0x034与0xD800相加,得到0xD834。这是U + 1D161的UTF-16表示形式的高字节。

对于低十位,我们得到0x161 + 0xDC00,即0xDD61

还有另一个问题,String.codePointAt将char索引作为参数。有时,一个代码点可能占用两个字符的空间,因此在增加0xFFFF之前,我们应检查当前代码点是否大于i

顺便说一句,如果您使用的是Java 1.8,则可以使用新的String.codePoints API,该API返回一个IntStream

答案 1 :(得分:1)

首先,您在问题中显示的字符是Unicode字符HEAVY BLACK HEART或U + 2764,因此其代码为0x2764。

然后,当您将字符转换为int时,将获得其代码点。因此,是的,(int) '\u2674' 是0x2674或十进制9844。所以得到它也就不足为奇了。

如果您要打印字符,则无需转换即可打印:

System.out.print(unicode);          // no end of line after the character
System.out.println(unicode);        // character followed with an end of line

答案 2 :(得分:1)

unicodeStr.getBytes()。length是与字符集有关的

检查此一项: Bytes of a string in Java