如何使用Unicode名称在Java中查找字符或int代码点?
例如,如果
Character.getName('\u00e4')
返回"LATIN SMALL LETTER A WITH DIAERESIS"
,如何使用“普通”Java执行反向操作(即从"LATIN SMALL LETTER A WITH DIAERESIS"
转到'\u00e4'
)?
编辑:为了阻止我想要或不想要的评论洪流,我将在Python中做些什么:
"\N{LATIN SMALL LETTER A WITH DIAERESIS}" # this gives me what I want as a literal
unicodedata.lookup("LATIN SMALL LETTER A WITH DIAERESIS") # a dynamic version
现在,问题是:在Java中做同样的事情。
而且,BTW,我不想“打印unicode转义” - 实际上获取字符的十六进制很容易,但我想要一个名字的字母。
换句话说我希望与Character.getName(int)
做的相反。
答案 0 :(得分:5)
ICU4J图书馆可以为您提供帮助。它有一个带有getCharFromName
的类UCharacter
和其他相关方法,可以将各种类型的字符名称字符串映射回它们所代表的int
代码点。
但是,如果您正在使用硬编码字符名称(即源代码中引用的字符串文字),那么进行一次翻译会更有效 - 在源代码中使用\u
转义符如有必要,添加带有全名的注释 - 而不是每次都在运行时产生解析名称表的成本。如果字符名称来自于读取文件或类似文件,那么显然您必须在运行时进行转换。
答案 1 :(得分:1)
对于JDK 9和更高版本,使用静态方法Character.codePointOf(String name)
是最简单的方法:
公共静态int codePointOf((字符串名称)
返回由给定Unicode字符名称指定的Unicode字符的代码点值。
这适用于所有Uniocde角色,而不仅限于基本多语言平面中的角色。例如,在Java 12上运行此代码...
String s1 = "LATIN SMALL LETTER A WITH DIAERESIS";
int cp1 = Character.codePointOf(s1);
System.out.println("Unicode name \"" + Character.getName(cp1) + "\" => code point " + cp1 + " => character " + Character.toString(cp1));
String s2 = "EYES";
int cp2 = Character.codePointOf(s2);
System.out.println("Unicode name \"" + Character.getName(cp2) + "\" => code point " + cp2 + " => character " + Character.toString(cp2));
String s3 = "DNA Double Helix"; // Only works with JDK12 and later. Otherwise java.lang.IllegalArgumentException is thrown.
int cp3 = Character.codePointOf(s3);
System.out.println("Unicode name \"" + Character.getName(cp3) + "\" => code point " + cp3 + " => character " + Character.toString(cp3));
...产生此输出...
Unicode name "LATIN SMALL LETTER A WITH DIAERESIS" => code point 228 => character ä
Unicode name "EYES" => code point 128064 => character ?
Unicode name "DNA DOUBLE HELIX" => code point 129516 => character ?
总结转换:
Character.getName(codepoint)
Character.toString(codepoint)
Character.codePointOf(name)
Character.toString(Character.codePointOf("LATIN SMALL LETTER A WITH DIAERESIS"));
。注意:
IllegalArgumentException
时使用{1}}。答案 2 :(得分:0)
好吧,看一下Character.class
的源代码:
public static String getName(int codePoint) {
if (!isValidCodePoint(codePoint)) {
throw new IllegalArgumentException();
}
String name = CharacterName.get(codePoint);
if (name != null)
return name;
...
}
CharacterName
是一个包私有类,它懒惰地初始化一个SoftReference<byte[]>
字符名称池(我认为)。但是,有一条线特别值得关注,它埋藏在一系列不同的输入流构造函数中:
private static synchronized byte[] initNamePool() {
...
return getClass().getResourceAsStream("uniName.dat");
...
}
现在,我一直在进行一些挖掘,由于某种原因,这个我将再看看源代码,但如果我能解决它,可能需要一段时间才能解码。uniName.dat
似乎并不存在于OpenJDK的源代码中。我找到了uniName.dat
- 作为我的TeX Live发行版的一部分,奇怪的是。在十六进制编辑器中打开它会显示混乱的字节 - 所以内容以某种方式编码。怎么样,我不知道。
此外,我的Eclipse副本中的调试器似乎已损坏(由于某种原因无法解析变量),因此我无法检查输入流以尝试查看它的位置&# 39;阅读。
所以简而言之,似乎你不能在本机Java中做到这一点,除非你想从CharacterName
复制粘贴名称池代码,或者滚动自己的代码来解密这个文件(假设你可以找到它)
修改:找到uniName.dat
!在我的机器上,位于Java安装中的resources.jar
。仍然是一堆字节。所以你可以自己解析这个文件(不是很有趣,涉及很多有点麻烦),或者使用一个库(上面建议)。因此,如果您仅限于本机Java,则可能需要查看CharacterName
类,看看是否可以将某些内容放入HashMap<String, Character>
。
答案 3 :(得分:0)
我希望仅依赖于“普通” Java的此类对某人有用。它利用了填充延迟的查找表,可以随时通过reset(false)
调用以释放可用内存来清除查找表(可以自动填充该表并在需要时再次使用它)。如果要查找的字符位于较低的Unicode块(通常是这种情况),则该表的填充时间几乎不明显。我添加了通过调用reset(true)
预填充整个表格的可能性。
还要注意,在U+0007
和U+1F514
之间存在Unicode name collision。 Java的Character.getName()
仍为前者返回“ BELL”。所呈现的类至少在反向操作中尝试解决此问题,为分配给它的批准的唯一名称“ ALERT”返回U+0007
。
import java.util.Map;
import java.util.HashMap;
public class UnicodeTable {
public static final char INVALID_CHAR = '\uFFFF';
private static final Map<String, Integer> charMap = new HashMap<>();
private static boolean incomplete;
private static int lastLookup;
static {
reset(false);
}
public static int getCodePoint(String name) {
Integer cp = charMap.get(name);
if (cp == null && incomplete) {
while (++lastLookup <= Character.MAX_CODE_POINT) {
String uName = Character.getName(lastLookup);
if (uName != null) {
charMap.put(uName, lastLookup);
if (uName.equals(name))
return lastLookup;
}
}
incomplete = false;
}
return cp == null ? INVALID_CHAR : cp;
}
public static char getChar(String name) {
int cp = getCodePoint(name);
return Character.isBmpCodePoint(cp) ? (char)cp : INVALID_CHAR;
}
private static final int ALERT = 0x000007;
private static final int BELL = 0x01F514;
public static void reset(boolean fillUp) {
if (!fillUp) {
charMap.clear();
incomplete = true;
lastLookup = Character.MIN_CODE_POINT - 1;
charMap.put("ALERT", ALERT);
String bName = Character.getName(BELL);
if (bName.equals(Character.getName(ALERT))) {
getCodePoint(bName);
charMap.put(bName, BELL);
}
} else if (incomplete) {
while (++lastLookup <= Character.MAX_CODE_POINT) {
String uName = Character.getName(lastLookup);
if (uName != null)
charMap.put(uName, lastLookup);
}
incomplete = false;
}
}
}