7

G-Clef (U+1D11E) 不是基本多语言平面(BMP) 的一部分,这意味着它需要超过 16 位。几乎所有 Java 的读取函数都只返回 achar或 aint只返回 16 位。哪个函数读取完整的 Unicode 符号,包括 SMP、SIP、TIP、SSP 和 PUA?

更新

我问过如何从输入流中读取单个 Unicode 符号(或代码点)。我既没有任何整数数组,也不想读取一行。

可以使用 构建代码点,Character.toCodePoint()但此功能需要char. 另一方面,读取 achar是不可能的,因为read()返回 a int。到目前为止,我最好的工作是这个,但它仍然包含不安全的演员表:

public int read_code_point (Reader input) throws java.io.IOException
{
  int ch16 = input.read();
  if (Character.isHighSurrogate((char)ch16))
    return Character.toCodePoint((char)ch16, (char)input.read());
  else 
    return (int)ch16;
}

如何做得更好?

更新 2

另一个返回字符串但仍使用强制转换的版本:

public String readchar (Reader input) throws java.io.IOException
{
  int i16 = input.read(); // UTF-16 as int
  if (i16 == -1) return null;
  char c16 = (char)i16; // UTF-16
  if (Character.isHighSurrogate(c16)) {
    int low_i16 = input.read(); // low surrogate UTF-16 as int
    if (low_i16 == -1)
      throw new java.io.IOException ("Can not read low surrogate");
    char low_c16 = (char)low_i16;
    int codepoint = Character.toCodePoint(c16, low_c16);
    return new String (Character.toChars(codepoint));
  }
  else 
    return Character.toString(c16);
}

剩下的问题是:演员表是否安全或如何避免他们?

4

2 回答 2

1

到目前为止,我最好的工作是这个,但它仍然包含不安全的演员表

您提供的代码唯一不安全的地方是,如果已达到 EOF ,则ch16可能为 -1 。input如果您首先检查此条件,则可以保证其他(char)强制转换是安全的,因为Reader.read() 指定返回 -1 或char(0 - 0xFFFF) 范围内的值。

public int read_code_point (Reader input) throws java.io.IOException
{
  int ch16 = input.read();
  if (ch16 < 0 || !Character.isHighSurrogate((char)ch16))
    return ch16;
  else {
    int loSurr = input.read();
    if(loSurr < 0 || !Character.isLowSurrogate((char)loSurr)) 
      return ch16; // or possibly throw an exception
    else 
      return Character.toCodePoint((char)ch16, (char)loSurr);
  }
}

这仍然不理想,实际上您需要处理第一个char读取是高代理但第二个不是匹配的低代理的边缘情况,在这种情况下您可能希望按char原样返回第一个并备份阅读器,以便下一次阅读为您提供下一个字符。但这仅在input.markSupported() == true. 如果你保证那么怎么样

public int read_code_point (Reader input) throws java.io.IOException
{
  int firstChar = input.read();
  if (firstChar < 0 || !Character.isHighSurrogate((char)firstChar)) {
    return firstChar;
  } else {
    input.mark(1);
    int secondChar = input.read();
    if(secondChar < 0) {
      // reached EOF
      return firstChar;
    } else if(!Character.isLowSurrogate((char)secondChar)) {
      // unpaired surrogates, un-read the second char
      input.reset();
      return firstChar;
    }
    else {
      return Character.toCodePoint((char)firstChar, (char)secondChar);
    }
  }
}

或者您可以将原始阅读器包装在 a 中PushbackReader并使用unread(secondChar)

于 2013-06-28T12:09:48.923 回答
-1

完整的 Unicode 可以用 UTF-8 和 UTF-16 表示,分别由字节序列表示。字节对(“java chars”)。可以从 String 中提取完整的 Unicode代码点

int[] codePoints = { 0x1d11e };
String s = new String(codePoints, 0, codePoints.length);

for (int i = 0; i < s.length(); ) {
    int cp = s.codePointAt(i);
    i += Character.charCount(cp);
}

对于具有基本拉丁字符的文件,UTF-8 似乎很好。

以下内容读取完整的标准 Unicode 文件(UTF-8 格式):

try (BufferedReader in = new BufferedReader(
        new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
    for (;;) {
        String line = in.readLine();
        if (line == null) {
            break;
        }
        ... do some thing with a Unicode line ...
    }
} catch (FileNotFoundException e) {
    System.err.println("No file: " + file.getPath());
} catch (IOException e) {
    ...
}

提供一个(或多个 Unicode 代码)的 Java 字符串的函数:

String s = unicodeToString(0x1d11e);
String s = unicodeToString(0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x1d11e);

public static String unicodeToString(int... codepoints) {
    return new String(codePoints, 0, codePoints.length);
}
于 2013-06-28T09:52:34.370 回答