【问题标题】:convert string contains ISO 8859-1 hex characters code to UTF-8 java将包含 ISO 8859-1 十六进制字符代码的字符串转换为 UTF-8 java
【发布时间】:2019-11-23 02:47:10
【问题描述】:

我有一个字符串,我认为它包含一些 ISO-8859-1 十六进制字符代码

String doc = "#xC1;o thun b#xE9; g#xE1;i c#x1ED9;t d#xE2;y xanh bi#x1EC3;n"

我想把它改成这个,

Áo thun bé gái cột dây xanh biển

我试过这个方法,但没有运气

byte[] isoBytes = doc.getBytes("ISO-8859-1");
System.out.println(new String(isoBytes, "UTF-8"));

转换它的正确方法是什么?非常感谢您的帮助!

【问题讨论】:

  • 那么你可以检查它是否正确转换...
  • 什么是#xC1;o?它是字面上的 6 个 Unicode 字符:哈希、x、C、1、分号、o?或者是你在这篇文章中的代表,比如一个值为 00C1 的角色?在后一种情况下应该没有问题:C1 在 Unicode 和 8859-1 中都是大写字母 A。
  • 您的字符串由字符引用组成。没有简单的方法可以用纯 Java 解析那些。您需要一个库或编写自己的解析器。
  • 嗯,它不是 ISO 8859-1,因为 #x1EC3;意味着 >255 个代码点。它看起来确实像 HTML 或 XML 十六进制数字字符实体引用,但缺少前导 &。在这种情况下,数字将是 Unicode 代码点。 (由于相似之处,您可能会将其与 ISO 8859-1 混淆。)这些数据来自哪里?格式是什么?
  • 实际上,U+1EC3 是拉丁文小写字母 E,上面带有圆形和钩子。见UnicodeData.txt

标签: java utf-8 iso-8859-1


【解决方案1】:

假设#nnnn; 序列是普通的旧Unicode 字符表示,我建议采用以下方法。

class Cvt {

    static String convert(String in) {
        String str = in;
        int curPos = 0;
        while (curPos < str.length()) {
            int j = str.indexOf("#x", curPos);
            if (j < 0) // no more #x
                curPos = str.length();
            else {
                int k = str.indexOf(';', curPos + 2);
                if (k < 0) // unterminated #x
                    curPos = str.length();
                else { // convert #xNNNN;
                    int n = Integer.parseInt(str.substring(j+2, k), 16);
                    char[] ch = { (char)n };
                    str = str.substring(0, j) + new String(ch) + str.substring(k+1);
                    curPos = j + 1; // after ch
                }
            }
        }
        return str;
    }

    static public void main(String... args) {
        String doc = "#xC1;o thun b#xE9; g#xE1;i c#x1ED9;t d#xE2;y xanh bi#x1EC3;n";
        System.out.println(convert(doc));
    }

}

这与上一个答案的方法非常相似,只是假设字符是 Unicode 代码点而不是 8859-1 代码点。

输出是

Áo thun bé gái cột dây xanh biển

【讨论】:

  • 好方法 - 请注意,这支持 Unicode BMP(基本多语言平面 - 代码点长达 16 位)。这可能就是 OP 所需要的。如果需要支持完整的 Unicode 字符集,则需要在将代码点放入 Java 字符串之前对代码点进行 UTF-16 编码。
  • 请注意,您可能希望在 StringBuikdee 中构建输出,最后只转换为 String 一次,以提高效率
  • 同意如果在性能敏感的上下文中频繁执行而不是一次性执行,则可以提高效率;我专注于“易于编写”:-) 请注意,我的方法的费用或多或少与字符串中“#x”字段的数量成正比:如果没有,则只需扫描一次即可发现,无需复制.
  • 还要注意错误处理不足:一个#xHAHA;,这段代码就结束了。改进留给 OP 练习!
【解决方案2】:

Java 中的字符串没有十六进制文字语法。如果您需要支持该字符串格式,我会创建一个辅助函数来解析该格式并构建一个字节数组,然后将其解析为 ISO-8859-1。

import java.io.ByteArrayOutputStream;

public class translate {

    private static byte[] parseBytesWithHexLiterals(String s) throws Exception {
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();

        while (!s.isEmpty()) {
            if (s.startsWith("#x")) {
                s = s.substring(2);
                while (s.charAt(0) != ';') {
                    int i = Integer.parseInt(s.substring(0, 2), 16);
                    baos.write(i);
                    s = s.substring(2);
                }

            } else {
                baos.write(s.substring(0, 1).getBytes("US-ASCII")[0]);
            }

            s = s.substring(1);
        }

        return baos.toByteArray();
    }

    public static void main(String[] args) throws Exception {
        String doc = "#xC1;o thun b#xE9; g#xE1;i c#x1ED9;t d#xE2;y xanh bi#x1EC3;n";
        byte[] parsedAsISO88591 = parseBytesWithHexLiterals(doc);
        doc = new String(parsedAsISO88591, "ISO-8859-1");

        System.out.println(doc); // Print out the string, which is in Unicode internally.

        byte[] asUTF8 = doc.getBytes("UTF-8"); // Get a UTF-8 version of the string.
    }

}

【讨论】:

  • 我看不出 ASCII 是从哪里出现的。输入字符串是 Unicode(因为它是字符串)。对于不属于#xNNN 的字符;序列,只需复制它们即可。
【解决方案3】:

在这种情况下,代码确实会掩盖需求。要求有点不确定,但似乎是解码类似于 HTML 和 XML 的专用 Unicode 字符实体引用,如 cmets 中所述。

这也是一种罕见的情况,正则表达式引擎的优势超过了理解模式语言所需的任何学习。

String input = "#xC1;o thun b#xE9; g#xE1;i c#x1ED9;t d#xE2;y xanh bi#x1EC3;n";

// Hex digits between "#x" and ";" are a Unicode codepoint value
String text = java.util.regex.Pattern.compile("(#x([0-9A-Fa-f]+);)")
    .matcher(input)
    // group 2 is the matched input between the 2nd ( in the pattern and its paired )
    .replaceAll(x -> new String(Character.toChars(Integer.parseInt(x.group(2), 16))));
System.out.println(text);

matcher 函数查找候选字符串以替换与模式匹配的字符串。 replaceAll 函数将它们替换为计算的 Unicode 代码点。由于 Unicode 代码点可能被编码为两个 char (UTF-16) 值,因此所需的替换字符串必须从 char[] 构造。

【讨论】:

    猜你喜欢
    • 2020-01-25
    • 1970-01-01
    • 1970-01-01
    • 2014-08-29
    • 2014-07-04
    • 2012-10-08
    • 2016-07-29
    • 2020-10-26
    相关资源
    最近更新 更多