问题描述
当访问Windows系统资源(与音频有关)时,我发现Windows使用其自己的字符集提供了上述资源的描述字符串,而Java则将这些字符串视为默认情况下的所有字符串:unicode编码。因此,我得到了很多问号,而不是有意义的文本:
????????? ???????? ???????
使用 String .codePointAt() 方法,我发现这些问题实际上隐藏了一些Windows-1252编码的文本。我当然想看哪个。因此,我开始努力将字符串转换为可读的内容。
半天后,在我为Stackoverflow和Google搜寻相关主题之后,我取得了一些进步,但这只会引发更多问题。所以,这是我的代码:
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import javax.sound.sampled.AudioSystem;
public class Study_Encoding {
//private static final Charset utf8Charset = Charset .forName ("UTF-8");
private static final Charset win1251Charset = Charset .forName ("Windows-1251");
private static final Charset win1252Charset = Charset .forName ("Windows-1252");
public static void main(String[] args) {
String str = AudioSystem .getmixerInfo () [0] .getName ();
System .out .println ("Original string:");
System .out .println (str + "\n");
System .out .println ("Its code-points:");
displayCodePointSequence (str);
System .out .println ("Windows-1251-decoded byte array (wrong):");
byte [] win1251ByteArr = str .getBytes (win1251Charset);
displayByteSequence (win1251ByteArr);
System .out .println ("Windows-1252-decoded byte array (right):");
byte [] win1252ByteArr = str .getBytes (win1252Charset);
displayByteSequence (win1252ByteArr);
System .out .println ("Windows-1252-encoded string (wrong):");
try {
System .out .println (win1252Charset .newDecoder ()
.decode (ByteBuffer .wrap (win1252ByteArr)) .toString () + "\n");
} catch (Exception e) {
System .out .println ("ERROR:" + e .toString ());
}
System .out .println ("Windows-1251-encoded string (right):");
try {
System .out .println (win1251Charset .newDecoder ()
.decode (ByteBuffer .wrap (win1252ByteArr)) .toString () + "\n");
} catch (Exception e) {
System .out .println ("ERROR:" + e .toString ());
}
}
private static void displayCodePointSequence (String str) {
if (null == str) {
System .out .println ("No string");
return;
}
if (str .isEmpty ()) {
System .out .println ("Empty string");
return;
}
for (int k = 0; str .length () > k; ++k) {
System .out .print (str .codePointAt (k) + " ");
}
System .out .println ("[" + str .length () + "]\n");
}
private static void displayByteSequence (byte [] byteArr) {
if (null == byteArr) {
System .out .println ("No array");
return;
}
if (0 == byteArr .length) {
System .out .println ("Empty array");
return;
}
for (int k = 0; byteArr .length > k; ++k) {
System .out .print ((((int) byteArr [k]) & 0xFF) + " ");
}
System .out .println ("[" + byteArr .length + "]\n");
}
}
该程序产生以下输出(最后一行是我一直想要得到的):
Original string:
????????? ???????? ???????
Its code-points:
207 229 240 226 232 247 237 251 233 32 231 226 243 234 238 226 238 233 32 228 240 224 233 226 229 240 [26]
Windows-1251-decoded byte array (wrong):
63 63 63 63 63 63 63 63 63 32 63 63 63 63 63 63 63 63 32 63 63 63 63 63 63 63 [26]
Windows-1252-decoded byte array (right):
207 229 240 226 232 247 237 251 233 32 231 226 243 234 238 226 238 233 32 228 240 224 233 226 229 240 [26]
Windows-1252-encoded string (wrong):
????????? ???????? ???????
Windows-1251-encoded string (right):
Первичный звуковой драйвер
由于任何人都可以看到win1251和win1252编码混合在一起的原因。另外,我猜想,有一种方法可以使Java程序将所有字符串视为某种本机编码中的字符串(我不希望!!!),或者至少是系统提供的。所以,...
...我的问题是:
编辑:
似乎我没有说清楚,但是我不是在谈论文本文件的内容,而是在谈论系统提供的字符串,例如设备的名称和描述(物理和虚拟),也许是文件名和目录名。在上面的示例中,字符串“Первичныйзвуковойдрайвер”应类似于英语Windows中的“默认音频设备”。
解决方法
这是一个令人费解的问题,但基础知识是:
- 没有编码就没有字符串。最常见的形式(c字符串)使用ASCII编码。 Java本机使用UTF16。
- 某些字符集之间没有完美的编码转换。例如ASCII-> EBCDIC-> ASCII由于这些字符集之间缺少1:1关系而导致字符串损坏。
- 对我来说,该文件似乎包含1个字符集的数据,并且您想将其转换为Java本机格式(UTF16)。这很简单。您可以使用FileInputStream读取字节数据。您可以使用Reader读取String数据。因此,您希望您的读者执行转换: https://docs.oracle.com/javase/8/docs/api/java/io/InputStreamReader.html#InputStreamReader(java.io.InputStream,%20java.nio.charset.Charset)
因此,基本上,您要使用的代码如下:
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(myFile),StandardCharsets.CHARSETOFCHOICE)))
{
String line;
while ((line = br.readLine()) != null)
{
// Do what you want with the string.
}
}
我将重申,取决于源/目标字符集,转换可能不完美,并且可能导致损坏。