Java中的UTF-8字符编码

cam*_*mbo 11 java character-encoding

我在将一些法语文本转换为UTF8时遇到了一些问题,因此可以在控制台,文本文件或GUI元素中正确显示.

原始字符串是

HANDICAP╔ES

应该是

HANDICAPÉES

这是一个代码片段,显示了我如何使用jackcess Database驱动程序读取Eclipse/Linux环境中的Acccess MDB文件.

Database database = Database.open(new File(filepath));
Table table = database.getTable(tableName, true);
Iterator rowIter = table.iterator();
while (rowIter.hasNext()) {
    Map<String, Object> row = this.rowIter.next();
    // convert fields to UTF
    Map<String, Object> rowUTF = new HashMap<String, Object>();
    try {
        for (String key : row.keySet()) {
            Object o = row.get(key);
            if (o != null) {
                String valueCP850 = o.toString();
                // String nameUTF8 = new String(valueCP850.getBytes("CP850"), "UTF8"); // does not work!
                String valueISO = new String(valueCP850.getBytes("CP850"), "ISO-8859-1");
                String valueUTF8 = new String(valueISO.getBytes(), "UTF-8"); // works!
                rowUTF.put(key, valueUTF8);
            }
        }
    } catch (UnsupportedEncodingException e) {
        System.err.println("Encoding exception: " + e);
    }   
}
Run Code Online (Sandbox Code Playgroud)

在代码中,您将看到我想直接转换为UTF8的位置,这似乎不起作用,因此我必须进行双重转换.另请注意,使用jackcess驱动程序时似乎没有办法指定编码类型.

谢谢,Cam

Ala*_*ore 9

基于新信息的新分析.
看起来您的问题在于文本存储在Access DB中之前的编码.它似乎已编码为ISO-8859-1或windows-1252,但解码为cp850,导致字符串HANDICAP?ES存储在DB中.

正确地从数据库中检索到该字符串后,您现在正在尝试反转原始编码错误并恢复应该存储的字符串:HANDICAPÉES.你用这条线完成了这个:

String valueISO = new String(valueCP850.getBytes("CP850"), "ISO-8859-1");
Run Code Online (Sandbox Code Playgroud)

getBytes("CP850")将字符?转换为字节值0xC9,并且String构造函数根据ISO-8859-1对其进行解码,从而生成字符É.下一行:

String valueUTF8 = new String(valueISO.getBytes(), "UTF-8");
Run Code Online (Sandbox Code Playgroud)

...什么也没做. getBytes()使用平台默认编码对字符串进行编码,该编码在Linux系统上为UTF-8.然后,String构造函数使用相同的编码对其进行解码.删除该行,您仍然应该得到相同的结果.

更重要的是,您尝试创建"UTF-8字符串"是错误的.您不需要关心Java字符串的编码 - 它们总是UTF-16.将文本带入Java应用程序时,您只需确保使用正确的编码对其进行解码.

如果我的分析正确,您的Access驱动程序正在正确解码它; 问题出在另一端,可能是在DB进入图片之前. 就是你需要解决的问题,因为new String(getBytes())在所有情况下都不能指望黑客行事.


原始分析,基于没有信息.: - /
如果你在HANDICAP?ES控制台上看到,可能没问题.鉴于此代码:

System.out.println("HANDICAPÉES");
Run Code Online (Sandbox Code Playgroud)

在将(Unicode)字符串发送到控制台之前,它将(Unicode)字符串转换为平台默认编码windows-1252.然后控制台使用自己的默认编码解码,恰好是cp850.所以控制台显示错误,但这是正常的.如果要正确显示,可以使用以下命令更改控制台的编码:

CHCP 1252
Run Code Online (Sandbox Code Playgroud)

要在GUI元素(如JLabel)中显示字符串,您不必执行任何特殊操作.只要确保使用可以显示所有字符的字体,但这对法语来说应该不是问题.

至于写入文件,只需在创建Writer时指定所需的编码:

OutputStreamWriter osw = new OutputStreamWriter(
    new FileOutputStream("myFile.txt"), "UTF-8");
Run Code Online (Sandbox Code Playgroud)


Bal*_*usC 8

String s = "HANDICAP?ES";
System.out.println(new String(s.getBytes("CP850"), "ISO-8859-1")); // HANDICAPÉES
Run Code Online (Sandbox Code Playgroud)

这显示正确的字符串值.这意味着它最初使用ISO-8859-1进行编码/解码,然后使用CP850进行了错误编码(最初CP1252也称为Windows ANSI,如注释中所指出的那样确实也是可能的,因为它具有与ISO-8859-1中相同的代码点).É

对齐环境和二进制管道以使用所有同一个字符编码.你不能也不应该在它们之间进行转换.您可能会以这种方式丢失非ASCII范围内的信息.

注意:请勿使用上面的代码片段来"修复"问题!那不是正确的解决方案.


更新:你显然仍在努力解决这个问题.我将重复答案的重要部分:

  1. 调整你的环境,二元管道使用所有一个相同的字符编码.

  2. 你可以,应该不是他们之间的转换.您可能会以这种方式丢失ASCII范围内的信息.

  3. 千万不要使用上面的代码片段,以"修复"的问题!那不是正确的解决方案.

要解决此问题,您需要选择要在整个应用程序中使用的字符编码X. 我建议UTF-8.更新MS Access以使用编码X.更新开发环境以使用编码X.更新java.io代码中的读取器和编写器以使用编码X.更新编辑器以使用编码X读取/写入文件.更新应用程序的用户界面以使用编码十,不要没有在某个步骤中使用Y或Z或什么的.如果某些数据存储区(MS Access,文件等)中的字符损坏,则需要通过手动替换数据存储区中的字符来修复它.不要为此使用Java.

如果您实际上使用"命令提示符"作为用户界面,那么您实际上已经丢失了.它不支持UTF-8.正如评论和评论中链接的文章中所建议的那样,您需要创建一个Swing应用程序,而不是依赖于受限制的命令提示符环境.

  • 您需要指示JDBC驱动程序和/或数据库使用正确的编码(数据库本身正在使用的编码!).UTF-8当然支持这些字符,但如果你理解我的意思,那么使用不同的二进制表示.字符即-as everything-作为字节传输.仅仅因为计算机不了解其他任何东西.[本文](http://balusc.blogspot.com/2009/05/unicode-how-to-get-characters-right.html)可能有助于更多地了解引擎盖下的问题. (2认同)