Java中的UTF-8转ASCII编码转换

3

我有一个包含UTF-8字符集格式的字符串。

String str = "100µF";

上述字符串的期望输出为"100µF"

我在 StackOverflow 上查找并获得以下代码

public static String decompose(String s) {
    return java.text.Normalizer.normalize(s, java.text.Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+","");
}

但是,我得到的上述字符串的输出是“100AµF”。


UTF-8不是字符集,而是Unicode字符集的几种字符编码之一。UTF-16是另一种编码方式,这是Java文本数据类型String、char和Character所使用的编码方式。 - Tom Blodget
根据您的示例代码,我们可以看出在将文本放入字符串之前,它已经被损坏了。请回溯并解决这个问题。如果您想提供有关上游过程的详细信息,请[编辑]您的问题。 - Tom Blodget
"100µF""100µF"的UTF-8编码形式。Java字符串是UTF-16编码的。如果您使用8位字符集不是UTF-8,错误地将UTF-8数据转换为UTF-16,则会在String中得到"100µF"。不要这样做!而且不要尝试修复"100µF" 转换为"100µF"(或任何其他类似损坏的字符串)。除非您能够确定使用了错误的字符集来破坏数据,否则猜测不会100%有效。您需要修复导致您首先获得错误的"100µF"的逻辑错误。 - Remy Lebeau
3个回答

6

这是一个XY问题

问题在于您的字符串是通过使用错误的字符集从字节创建的,该字符集假定一个字节就是一个字符,例如ISO 8559-1

但是这些字节不是ASCII码,也不是ISO 8859-1。这些字节是文本的UTF-8表示。

不要替换任何字符。不要规范化字符串。唯一正确的解决方法是将错误解码的字符串恢复为字节,然后使用UTF-8正确解码这些字节:

byte[] originalBytes = str.getBytes(StandardCharsets.ISO_8859_1);

str = new String(originalBytes, StandardCharsets.UTF_8);

1
这是唯一一个带有正确分析的答案。然而,根据样本数据,不能确定应该使用ISO 8859-1来撤销损坏。我的系统有8种字符编码可以纠正这个样本:windows-1250、windows-1252、windows-1254、windows-1258、iso-8859-1、iso-8859-3、iso-8859-9和iso-8859-15。最多只有其中一种可能是正确的。@dev22intellial,如果你找不到错误的代码,你可以尝试输入一个全面的测试数据集(比如一个包含字节0-255的文件),并检测是否可以通过恰好一个字符编码来逆转它。 - Tom Blodget
或者,假设String是通过简单地将原始字节扩展为16位字符而创建的,而不考虑任何字符集,则可以分配一个相同长度的byte[]数组,然后将每个16位字符截断回8位字节。 - Remy Lebeau

1

ASCII中没有µ字符,因此无法用ASCII编写它。

Java String是Unicode字符序列(内部编码为UTF-16),所以你面临的问题取决于如何读取这个字符串或者如何写入它。

通常通过创建一个OutputStreamWriter(OutputStream out, String charsetName)InputStreamReader(InputStream in, String charsetName)并设置正确的字符集来解决这个问题。

因此,例如如果你从一个UTF-8编码的文件获取你的字符串,你应该创建一个读取器如下:

Reader in = new InputStreamReader(new FileInputStream('some_file.txt'),"UTF-8")

如果您要写入ISO-Latin-1文件,则应按如下方式创建Writer:

Writer out = new OutputStreamWriter(new FileOutputStream('some_file.txt'),"ISO-8859-1")

类似的情况也可能发生在HTTP请求/响应中,具体取决于应用服务器或浏览器如何解释每个请求/响应的正文。如果这是您的情况,则需要在问题中添加一些细节。

0

您正在处理 µ(U+00B5,微符号)和 Â(U+00C2,带抑扬符号的拉丁大写字母A)这两个字符,它们都属于 Latin-1 补充 Unicode 块

如果想允许µ但禁止Â,则需要自行进行过滤。对于每个字符,不会有单独的字符组(\p{})。

一种方法是定义一个白名单过滤器:

String input = "100µF";
String allowedFilter = "[^\\p{ASCII}µ]"; // regular ASCII + µ sign
String output = input.replaceAll(allowedFilter, "");
System.out.println(output); // 100µF

请注意,µÂ都可以在扩展ASCII中表示,因此仅过滤其中一个而不是另一个是违反直觉的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接