如何通过Java中的(unicode)名称获取字符?我需要与Character.getName相反(int codePoint)

时间:2014-05-15 07:02:05

标签: java unicode

如何使用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)做的相反。

4 个答案:

答案 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 ?

总结转换:

  • 对于代码点=> Unicode名称,请使用Character.getName(codepoint)
  • 对于代码点=>字符表示,请使用Character.toString(codepoint)
  • 对于Unicode名称=>代码点,请使用Character.codePointOf(name)
  • 对于Unicode名称=>字符表示,当前不存在JDK方法。而是使用Unicode名称的代码点间接执行此操作,如上所示。例如:Character.toString(Character.codePointOf("LATIN SMALL LETTER A WITH DIAERESIS"));

注意:

  • 确保所使用的JDK版本支持指定的Unicode名称。例如,将具有Unicode名称​​“ DNA Double Helix” 的字符添加到Unicode 11中,只有大于等于12的JDK版本才支持该字符。如果使用较早的JDK版本运行,则会得到{致电IllegalArgumentException时使用{1}}。
  • 如果在Unicode字符处显示白色正方形,请尝试更改字体(例如, Segoe UI Emoji 用于呈现Emoji字符)。

答案 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+0007U+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;
        }
    }
}
相关问题