Search code examples
javaencodingmojibake

Java convert encoding


I have a string which used to be an xml tag where mojibakes are contained:
<Applicant_Place_Born>Ð&#156;оÑ&#129;ква</Applicant_Place_Born>

I know that exactly the same string but in correct encoding is:
<Applicant_Place_Born>Москва</Applicant_Place_Born>

I know this because using Tcl utility I can convert it into proper string:

# The original string
set s "Ð&#156;оÑ&#129;ква"
# substituting the html escapes
set t "Ð\x9cоÑ\x81ква"
# decode from utf-8 into Unicode
encoding convertfrom utf-8 "Ð\x9cоÑ\x81ква"
Москва

I tried different variations of this:

System.out.println(new String(original.getBytes("UTF-8"), "CP1251"));

but I always got other mojibakes or question marks instead of characters.

Q: How can I do the same as Tcl does but using Java code?

EDIT:

I have tried @Joop Eggen's approach:

import org.apache.commons.lang3.StringEscapeUtils;


public class s {
    static String s;
    public static void main(String[] args) {
        try {
            System.setProperty("file.encoding", "CP1251");
            System.out.println("JVM encoding: " + System.getProperty("file.encoding"));
            s = "Ð&#156;оÑ&#129;ква";
            System.out.println("Original text: " + s);

            s = StringEscapeUtils.unescapeHtml4(s);
            byte[] b = s.getBytes(StandardCharsets.ISO_8859_1);
            s = new String(b, "UTF-16BE");

            System.out.println("Result: " + s);

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

The converted string was something Chineese:

JVM encoding: CP1251 Original text: Ð&#156;оÑ&#129;ква Result: 킜킾톁킺킲킰


Solution

  • A String in java should always be correct Unicode. In your case you seem to have UTF16BE interpreted as some single-byte encoding.

    A patch would be

    String string = new StringEscapeUtils().UnescapeHTML4(s);
    byte[] b = string.getBytes(StandardCharsets.ISO_8859_1);
    string = new String(b, "UTF-16BE");
    

    Now s should be a correct Unicode String.

    System.out.println(s);
    

    If the operating system for instance is in Cp1251 the Cyrillic text should be converted correct.

    • The characters in s are actually bytes of UTF-16BE I guess
    • By getting the bytes of the string in an single-byte encoding hopefully no conversion takes place
    • Then make a String of the bytes as being in UTF-16BE, internally converted to Unicode (actually UTF-16BE too)