【问题标题】:Reading/writing undefined unicode characters to a file读取/写入未定义的 unicode 字符到文件
【发布时间】:2021-04-25 20:58:41
【问题描述】:

短版:

将包含值 55296 和 57343(含)之间的字符的字符串写入文件会导致这些字符被问号 ('?') 替换,这意味着无法将生成的文件读回原始字符串。

如何避免/规避这个问题?

长版:

我有一个介于 0 和 65535(含)之间的整数数组,我正在尝试将其写入文件,以便稍后检索该数组。下面是一个示例数组:

int[] integerArray = new int[] {2404,44698,55597,17382,35641,10988};

了解 unicode 正好有 65536 个字符,而 Java 的 char 数据也有 65536 个值,我决定通过将这些整数中的每一个转换为一个字符,将它们添加到一个字符串,然后将该字符串写入一个文件来做到这一点像这样:

private static void writeUnicodeToFile(int[] array, String path) {
    String unicode = "";
    for(int integer : array) unicode += (char) integer;
    try { Files.write(Path.of(path), unicode.getBytes());
    } catch(IOException e) { e.printStackTrace(); }
}

这在大多数情况下是有效的,但是在示例数组的情况下,转换值 55597 会返回问号 ('?') 字符,因此当我尝试像这样检索值时:

private static int[] getUnicodeFromFile(String path) {
    String unicode = "";
    try { unicode = Files.readString(Path.of(path));
    } catch(IOException e) { e.printStackTrace(); }
    int[] integerArray = new int[unicode.length()];
    for(int i = 0; i < unicode.length(); i++) integerArray[i] = (int) unicode.charAt(i);
    return integerArray;
}

返回的数组在索引 2 处包含 63(问号的 unicode id)而不是 55597。

在 unicode 字符表中搜索值 55597 发现它不是一个有效字符,进一步实验发现 unicode 中有 2049 个字符将作为问号写入文件(包括问号本身) .所有其他字符都可以正常读写。

但是,这意味着无法将文件解码回整数数组,因为一个字符可以解释为其他 2048 个字符。

那么我怎样才能将所有这些字符彼此区分开来,以便我可以在文件中读取/写入它们而不会相互干扰呢?

或者,我可以以某种方式使用不同的字符集来完全避免这个问题吗?我可以灵活地将每个整数转换为从 0 到 255 的两个值,并使用其他一些文本文件编码设置。

其他可能相关的信息:

我正在使用 Eclipse IDE,并将我的工作区的文本文件编码设置为 UTF-8(我理解它等同于 unicode)。

【问题讨论】:

  • 关于 "...unicode 正好有 65536 个字符...",这从来都不是真的。大约 25 年前,Unicode 的理论最大字符集大小为 65,536,但现在没有实际限制。请参阅How many Unicode characters are there? 这与您的问题没有直接关系,但仍然值得澄清。

标签: java unicode character-encoding


【解决方案1】:

好的,这里是:

我有一个可行的解决方案,但它不是最佳的,所以我仍然会感谢人们提供的任何其他解决方案。

我没有尝试直接将 unicode 写入文件,而是使用以下代码将每个整数转换为两个字节:

//Get bytes from the data
private static byte[] decombineData(int[] data) {
    byte[] dataBytes = new byte[data.length*2]; // Create an array of bytes to write to
    //Write to each byte using the data in the string
    for(int i = 0; i < data.length; i++) {
        byte[] twoBytes = decombineBytes((int)data[i]); // Decombine the bytes of a character
        dataBytes[i*2] = twoBytes[0];
        dataBytes[i*2+1] = twoBytes[1];
    }
    return dataBytes; // Return the array of bytes
}

//Take a value from 0 to 65536 inclusively and return two bytes that would combine to make that value
private static byte[] decombineBytes(int combinedValue) {
    return new byte[] {(byte)(combinedValue/256-128), (byte)(combinedValue%256-128)};
}

在整数数组上调用 decombineData 将产生一个字节数组。这是反向应用它的方法:

// Turn an array of compressed bytes into a set of integers
private static int[] combineData(byte[] bytes) {
    int[] integers = new int[(bytes.length+1)/2]; // Initialise the return string
    for(int i = 0; i < bytes.length; i+=2) { // Create the return string from the compressed bytes
        if (i+1 != bytes.length) { integers[i] = combineBytes(bytes[i],bytes[i+1]); } // Compress two bytes if enough characters exist
        else { integers[i] = (combineBytes(bytes[i],(byte)0)); } // Otherwise only compress one byte
    }
    return integers;
}

//Combine two bytes into a single integer from 0 to 65536 inclusively
private static int combineBytes(byte byteOne, byte byteTwo) {
    return (byteOne*256)+byteTwo+32896;
}

要写入文件,您可以使用与以前相同的代码,但直接使用字节而不是 .getBytes():

private static void writeBytesToFile(byte[] bytes, String path) {
    try { Files.write(Path.of(path), bytes);
    } catch(IOException e) { e.printStackTrace(); }
}

还有类似的阅读方法:

public static byte[] readBytesFromFile(String path) {
    try { return Files.readAllBytes(Path.of(path));
    } catch (IOException e) { e.printStackTrace(); System.exit(1); }
    return null;
}

【讨论】:

    猜你喜欢
    • 2011-02-19
    • 2017-09-07
    • 1970-01-01
    • 2019-09-04
    • 1970-01-01
    • 2011-05-17
    • 1970-01-01
    • 2018-03-22
    • 1970-01-01
    相关资源
    最近更新 更多