Java:PHP的ord()的实现产生了超出ASCII的字符的不同结果

Java: Implementation of PHP's ord() yields different results for chars beyond ASCII

我正在尝试编写一个相当于PHP的EDCOX1第0章的Java:

1
2
3
4
5
6
7
public static int ord(char c) {
    return (int) c;
}

public static int ord(String s) {
    return s.length() > 0 ? ord(s.charAt(0)) : 0;
}

对于顺序值高达127的字符(即在ASCII中),这似乎很有效。但是,对于扩展的ASCII表或更高的表中的字符,php返回195(及更高版本)。Llama先生对相关问题的答案的评论解释如下:

To elaborate, the reason é showed ASCII 195 is because it's actually a two-byte character (UTF-8), the first byte of which is ASCII 195. – Mr. Llama

因此,我修改了我的ord(char c)方法,以屏蔽除最重要字节之外的所有字节:

1
2
3
public static int ord(char c) {
    return (int) (c & 0xFF);
}

不过,结果不同。两个例子:

  • ord('é')(U+0E9)在PHP中给出EDCOX1,2,而我的Java函数产生EDCOX1〔6〕。
  • ord('?')(U+2E06)在PHP中给出EDCOX1,8,而我的Java函数产生EDCOX1〔9〕。

我试图通过首先将String转换为byte数组(显式使用utf-8编码)来获得接受String的方法的相同行为:

1
2
3
public static int ord(String s) {
    return s.length() > 0 ? ord((char)s.getBytes(StandardCharsets.UTF_8)[0]) : 0;
}

但是,使用接受char的方法仍然和以前一样,我还没有找到解决方法。另外,我不明白这个变化为什么会起作用:不管怎样,Charset.defaultCharset()还是在我的平台上返回UTF-8。所以…

  • 如何使我的函数行为类似于PHP?
  • 为什么对ord(String s)的更改实际上有效?

解释性的答案是非常感谢的,因为我想知道到底发生了什么。


在爪哇中,EDCOX1×0是UTF 16编码单元。将utf-16转换成utf-8不仅仅是& 0xFF,例如,utf-16中的01FF是utf-8中的C7 BF,所以php ord()应该给0xC7(199),而0x01FF & 0xFF是255。

String版本可以工作,因为它实际上正在转换为utf-8。

最简单的方法是反转两个重载,因为String有一个方便的方法来获取utf-8:

1
2
3
public static int ord(String s) {
    return s.length() > 0 ? (s.getBytes(StandardCharsets.UTF_8)[0] & 0xff) : 0;
}

char转换为String

1
2
3
public static int ord(char c) {
    return c < 0x80 ? c : ord(Character.toString(c))
}

虽然这是可行的,但由于不必要的char→string→int转换,所以效率不高。Unicode码位c的utf-8编码的第一个字节实际上可以通过以下方式找到:

1
2
3
4
5
6
7
8
9
if (c < 0x80) {
    return c;
} else if (c < 0x800) {
    return 0xc0 | c >> 6;
} else if (c < 0x10000) {
    return 0xe0 | c >> 12;
} else {
    return 0xf0 | c >> 18;
}

您可能还想读取什么是unicode、utf-8、utf-16?获取一些背景信息。