Строка Java: обработка/преобразование собственной кодировки символов системы

При доступе к системным ресурсам 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-программу обрабатывать все строки как строки в какой-то собственной кодировке (чего я НЕ ХОЧУ!!!) или, по крайней мере, предоставляемой системой как единое целое. Так,...

... мои вопросы:

  1. Как преобразовать строку? (Что я решил, я думаю)
  2. Что происходит? (со смешанными кодировками и всем остальным)
  3. Как это сделать правильно? (Получение строки, если нет, преобразование строки)

РЕДАКТИРОВАТЬ:

Кажется, я не ясно выразился, но я говорю не о содержимом текстовых файлов, а о системных строках, таких как имена и описания устройств (физических и виртуальных), может быть, имена файлов и каталогов. В приведенном выше примере строка «Первичный звуковой драйвер» должна быть чем-то вроде «Аудиоустройство по умолчанию» в английской версии Windows.


person user14169057    schedule 26.08.2020    source источник


Ответы (1)


Это запутанный вопрос, но основы таковы:

  1. Нет такой вещи, как строка без кодировки. Наиболее распространенная форма (c-строка) использует кодировку ASCII. Java изначально использует UTF16.
  2. Идеального преобразования кодировки между определенными наборами символов не существует. Например, ASCII -> EBCDIC -> ASCII приводит к повреждению строки из-за отсутствия соотношения 1:1 между этими наборами символов.
  3. Мне кажется, что файл содержит данные в 1 наборе символов, и вы хотите преобразовать его в родную форму Java (UTF16). Это очень просто. Вы можете использовать FileInputStream для чтения байтовых данных. Вы можете использовать Reader для чтения строковых данных. Следовательно, вы хотите, чтобы ваш ридер выполнял преобразование: 20java.nio.charset.Charset)" rel="nofollow noreferrer">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.
   }
}

Я повторю, что преобразование может быть несовершенным в зависимости от исходного/целевого набора символов и может привести к повреждению.

person John    schedule 26.08.2020