Java char设置编码问题(从UTF8到cp866)

时间:2011-01-24 13:41:23

标签: java character-encoding

如何将文本从utf8 / cp1251(windows cyrillic)转换为DOS Cyrillic(cp866)

我找到了这个例子:

Charset fromCharset = Charset.forName("utf8");
Charset toCharset = Charset.forName("cp866");

String text1 = "Николай"; // my name in bulgarian
String text2 = "Nikolay"; // my name in english

System.out.println("TEXT1 :[" + toCharset.decode(fromCharset.encode(text1)).toString() + "]");
System.out.println("TEXT2 :[" + toCharset.decode(fromCharset.encode(text2)).toString() + "]");

输入是:

TEXT1 :[╨Э╨╕╨║╨╛╨╗╨░╨╣] // WRONG
TEXT2 :[Nikolay]  // CORRECT

问题出在哪里?

5 个答案:

答案 0 :(得分:13)

第一个:如果你有一个String对象,那么它就不再有编码了,它是一个纯Unicode字符串(*)!

在Java中,当您从字节(byte[])转换为字符串(String)时,编码仅用于 ,反之亦然。 (理论上你可以从byte[]直接转换为byte[],但我还没有看到用Java完成转换。

如果你有一些cp1251编码数据,那么它必须是byte[](即一个字节数组)或某种流(例如,作为{提供给你) {1}})。

如果你想提供一些数据为cp866,那么你必须提供它作为InputStream或某种流(例如`OutputStream)。

另外:没有“utf8 / cp1251”这样的东西。 UTF-8和CP-1251是几乎不相关的字符编码。您的输入是UTF-8或CP-1251(或其他)。它不可能都是(+)。

这是强制性链接:The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

(*)是的,严格来说它有一个编码,它是UTF-16,但是对于大多数用途,你可以(而且应该)把它想象成“无编码的理想Unicode字符串”
(+)严格来说,如果它只使用在两种编码中编码为相同字节的字符,通常是ASCII子集

答案 1 :(得分:5)

问题是你正在尝试解码一个编码的输出,就好像它是一个不同的编码。

想象一下,你有一个只能写出JPEG的程序,另一个只能读取PNG的程序......你希望能用第二个程序读取第一个程序的输出吗?

在这种情况下,两种编码碰巧兼容ASCII字符,但从根本上说你做错了。

如果您的文本已经是UTF-8,您应该使用UTF-8编码从二进制数据读取到Unicode字符串,然后使用其他编码将其写出二进制数据。 Unicode是基本的中间步骤,作为Java的本机文本格式。这相当于将JPEG输出加载到另一个程序中,该程序可以在您使用第二个应用程序读取之前执行转换为PNG。

答案 2 :(得分:4)

简短解决您的问题:

 System.out.write("ВАСЯ\n".getBytes("cp866")); // its right
 System.out.println("ВАСЯ".getBytes("cp866")); // its wrong

cmd.exe的结果:

C:\ Documents and Settings \ afram \Моидокументы\ NetBeansProjects \ Encoding \ dist> java -jar Encoding.jar

ВАСЯ

[B @ 1bab50a

答案 3 :(得分:3)

<强>短

将utf8 String解码为cp866。由于utf8和cp866只共享ascii符号,所以其他一切都会被破坏。

<强>长:

Java在内部使用UTF-16表示字符串,所有String对象都以UTF-16编码。

Charset.encode()创建一个包含选择编码中的String的bytebuffer,在您的代码中,它将Java UTF-16字符串转换为utf-8编码的字节数组。

Charset.decode()将一个bytebuffer编码为Charset,并将其转换为Java UTF-16字符串。在您的情况下,您使用utf-8解码器解码cp866字符串,从而导致错误的字符串。

由于java Strings具有指定的编码,因此在读取或写入时必须指定它。 InputStreamReader和OutputStreamWriter都为ctors提供了Charset参数。

这里有一个关于如何转换文件/流的示例。

//input the source is encoded in fromCharset
BufferedReader in = new BufferedReader(new InputStreamReader(...,fromCharset));
//output the target will be encoded in toCharset
PrintWriter out = new PrintWriter(new OutputStreamWriter(...,toCharset));
//reads a decoded String
String line = in.readLine();
while(line != null)
{
   out.println(line);
   line = in.readLine();
}

答案 4 :(得分:0)

问题是,你的控制台输出不是cp866。控制台是一个,转换是其他。

java中的内部字符串始终是unicode,charset对于输入/输出操作很重要。您尚未指定要对“转换”字符串执行的操作,但您应该明确地看到类InputStreamReader / OutputStreamWriter。它们为您的I / O操作提供字符集设置。