我对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,但显然我也不知道我在做什么。如果有人能指出我正确的方向,我将永远感激不已。目的是将字符串分割成多个字符,然后通过开关盒将每个字符转换成一个大字母。
答案 0 :(得分:2)
根据specification,getBytes()
使用平台默认值字符集对字符串进行编码,这与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