Java 给定一个字符集,如何将单个字符代码转换为“char”?

Java 给定一个字符集,如何将单个字符代码转换为“char”?,java,ascii,Java,Ascii,我想将十进制转换为ascii,这是返回意外结果的代码。这是我正在使用的代码 public static void main(String[] args) { char ret= (char)146; System.out.println(ret);// returns nothing. 我希望得到字符单“'”根据 有人见过这个吗?谢谢。您所指的页面提到,值160到255对应于ISO-8859-1(又名拉丁语1)表;对于128到159范围内的值,它们来自于拉丁语1的Wind

我想将十进制转换为ascii,这是返回意外结果的代码。这是我正在使用的代码

public static void main(String[] args) {
    char ret= (char)146;  
    System.out.println(ret);// returns nothing. 
我希望得到字符单“'”根据
有人见过这个吗?谢谢。

您所指的页面提到,值160到255对应于ISO-8859-1(又名拉丁语1)表;对于128到159范围内的值,它们来自于拉丁语1的Windows特定变体(ISO-8859-1未定义该范围,由操作系统指定)


Java字符基于UTF16,UTF16本身基于Unicode表。如果要特别引用右引号字符,可以在Java中将其指定为
'\u2019'
(请参阅)。

因此,有几件事需要注意

首先,您链接到的页面说明了有关代码点范围的问题:

扩展ASCII码(字符码128-255)

8位ASCII表有几种不同的变体。下表符合ISO 8859-1,也称为ISO拉丁语-1。代码128-159包含Microsoft®Windows Latin-1扩展字符

这是不正确的,或者至少对我来说,是误导性的措辞(和)。这已经是自找麻烦了。如果您通过
String
进行转换,也可以看到这一点:

String s = new String(new byte[] {(byte)146}, "iso-8859-1");
System.out.println(s);
输出相同的“意外”结果。看起来他们实际上指的是集合(也称为“Windows Latin-1”,但这个名称现在几乎完全过时),它确实将代码点定义为正确的单引号(对于在146处提供此字符的其他字符集,请查看并查找在0x92处提供此字符的编码),我们可以这样验证:

String s = new String(new byte[] {(byte)146}, "windows-1252");
System.out.println(s);
所以第一个错误是页面混乱

但最大的错误是你不能用你现在的方式去做你想做的事情。Java中的
char
是一个UTF-16代码点(或者一个的一半,如果您表示补充字符>0xFFFF,则单个
char
对应一个BMP点,一对字符或一个
int
对应整个范围,包括补充字符)

不幸的是,Java并没有为单字符转换公开很多API。甚至没有任何现成的方法可以将您选择的字符集转换为UTF-16

因此,一种选择是通过上面示例中暗示的
String
执行,例如,将代码点表示为原始
byte[]
数组并从中转换:

String s = new String(new byte[] {(byte)146}, "windows-1252");
System.out.println(s);
char c = s.charAt(0);
System.out.println(c);
您可以通过
s.charAt(0)
再次抓取
char
。请注意,在执行此操作时,您必须注意您的角色集。这里我们知道我们的字节序列对于指定的编码是有效的,并且我们知道结果只有一个
char
long,所以我们可以这样做

然而,在一般情况下,你必须小心。例如,可能字节序列和字符集产生的结果在UTF-16补充字符范围内。在这种情况下,
s.charAt(0)
不够,需要存储在
int
中的
s.codepoint(0)

另一种选择是,使用相同的警告,您可以使用解码,尽管它同样笨重,例如:

Charset cs = Charset.forName("windows-1252");
CharBuffer cb = cs.decode(ByteBuffer.wrap(new byte[] {(byte)146}));
char c = cb.get(0);
System.out.println(c);
请注意,我不完全确定
Charset#decode
如何处理补充字符,现在无法真正测试(但任何人都可以随意插话)


顺便说一句:在您的例子中,直接转换到
char
的146(0x92)对应于UTF-16字符“PRIVATE USE TWO”(),并且您将在那里显示的内容的所有赌注都被取消。这个字符是,而且似乎属于ANSI终端控制保留的字符范围(虽然实际上没有使用AFAIK,但无论如何它都在该范围内)。如果某些地区的浏览器将其作为一个正确的单引号来表示兼容性,我不会感到惊讶,但终端对它做了一些奇怪的事情

另外,仅供参考,官方的UTF-16代码点。通过使用该值,您可以可靠地将其存储在
字符中,例如:

System.out.println((char)0x2019);
通过查看从windows-1252转换后的值,您也可以自己看到这一点:

String s = new String(new byte[] {(byte)146}, "windows-1252");
char c = s.charAt(0);
System.out.printf("0x%x\n", (int)c); // outputs 0x2019
或者,为了完整性:

String s = new String(new byte[] {(byte)146}, "windows-1252");
int cp = s.codePointAt(0);
System.out.printf("0x%x\n", cp); // outputs 0x2019

“扩展ASCII”用词不当。根据定义,ASCII最多为127。有一些字符集可以扩展这个范围,但它们如何做到这一点差异很大。所以你真的需要知道你在说什么。您在代码中所做的是打印Unicode代码点146,它与单个上引号一致,幸运的是:也许您的答案在这里:在我的输入中,小数大于127。有些字符转换正确,但有些像“146”这样的字符会出现问题。您的字符不是ASCII。他们很可能是。在windows-1252字符集中,146实际上是
'\u2019'
。如果要查看正确的字符,请将代码更改为
(char)0x2019
'\u2019'
。您的终端可能使用的编码与您假设其数字代码将输出所需字符的终端不同。因此,程序和终端都做了正确的事情,只是它们没有按照您期望的方式解释数值,因为它们使用了不同的编码。阅读这篇信息性文章在我脑海中提出了一个问题:为什么语言允许(char)类型强制转换,或者,如果允许,为什么不需要,或者至少允许指定编码,例如(char:cp1252)integer_变量或其他任何变量。有几十种单字节编码方案:@Thomas嗯,它允许强制转换,因为没有理由不这样做,而且它必须是一种奇怪的特殊情况,因为在所有其他情况下,兼容类型之间的强制转换都是允许的。这将是不必要的限制,并以实现特殊情况编译器错误为代价