Как записать символ с кодовой точкой 80 в файл в Windows -1252? - PullRequest
0 голосов
/ 09 марта 2020

Я пытаюсь записать байты в файл в кодировке windows -1252. Приведенный ниже пример, записывающий необработанные байты с плавающей точкой в ​​файл, аналогичен тому, что я делаю в моей настоящей программе.

В данном примере я пишу необработанный гекс 1,0f для проверки .текст. Поскольку необработанный гекс 1,0f равен 3f 80 00 00 , я ожидаю получить ? € (NUL) (NUL) , как видно из Windows 1252 Статья в Википедии , 0x3f должна соответствовать '? ', 0x80 должна соответствовать '' и 0x00 - это ' NUL '. Все идет хорошо, пока я на самом деле не пытаюсь записать в файл; в этот момент я получаю исключение java .nio.charset.UnmappableCharacterException на консоли, и после того, как программа остановится на этом исключении, в файле будет только один символ '?' в этом. Полный вывод консоли находится под кодом ниже.

Похоже, Java считает кодовую точку 0x80 не отображаемой в кодовой странице windows -1252. Однако это кажется неправильным - все кодовые точки должны отображаться в реальные символы в этой кодовой странице. Проблема определенно связана с кодовой точкой 0x80 , как будто я пытаюсь с 0,5f ( 3f 00 00 00 ), он с удовольствием пишет ? (NUL) (NUL) ( NUL) в файл, и не выдает исключение. Эксперимент с другими кодовыми страницами тоже не работает; Если посмотреть на кодировки ключей, поддерживаемые Java языком здесь , то только серия UTF не даст мне исключения, но из-за их кодировки они не дают мне кодовую точку 0x80 в фактический файл.

Я попытаюсь использовать вместо этого байты, чтобы мне не приходилось беспокоиться о строковом кодировании, но кто-нибудь может сказать мне, почему мой код ниже дает мне исключение?

Код:

import java.io.IOException;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;

public class CharsetTest {
    public static void main(String[] args) {
        float max = 1.0f;
        System.out.println("Checking " + max);
        String stringFloatFormatHex = String.format("%08x", Float.floatToRawIntBits(max));
        System.out.println(stringFloatFormatHex);
        byte[] bytesForFile = javax.xml.bind.DatatypeConverter.parseHexBinary(stringFloatFormatHex);
        String stringForFile = new String(bytesForFile);
        System.out.println(stringForFile);

        String charset = "windows-1252";
        try {
            Writer output = Files.newBufferedWriter(Paths.get("test.txt"), Charset.forName(charset));
            output.write(stringForFile);
            output.close();
        } catch (IOException e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
    }
}

Консольный выход:

Checking 1.0
3f800000
?�  
Input length = 1
java.nio.charset.UnmappableCharacterException: Input length = 1
    at java.nio.charset.CoderResult.throwException(CoderResult.java:282)
    at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:285)
    at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
    at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
    at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
    at java.io.BufferedWriter.close(BufferedWriter.java:265)
    at CharsetTest.main(CharsetTest.java:21)

1 Ответ

0 голосов
/ 09 марта 2020

Редактировать: Проблема в инструкции String stringForFile = new String(bytesForFile);, ниже DatatypeConverter. Поскольку я строил строку без предоставления набора символов, он использует мой набор символов по умолчанию (UTF-8), в котором нет символа для кодовой точки 80 . Тем не менее, он создает исключение только при записи в файл. Это не происходит в приведенном ниже коде, потому что мой рефакторинг (учитывая предложение Йоханнеса Куна в комментариях) не использует конструктор String(byte[]) без указания кодировки.

* 1013 Йоханнеса Куна * предложение о конструкторе String(byte[]) дало мне несколько хороших подсказок. В итоге я получил следующий код, который выглядит так, как будто он работает нормально: даже печать символа на консоли и запись его в test.txt. Это говорит о том, что кодовую точку 80 можно перевести с помощью кодовой страницы windows -1252.

Если бы я догадался, почему этот код работает, а другой нет, я бы Я все еще буду сбит с толку, но я думаю, что это было что-то вроде преобразования в javax.xml.bind.DatatypeConverter.parseHexBinary(stringFloatFormatHex);. Похоже, это главное отличие, хотя я не уверен, почему это важно.

В любом случае приведенный ниже код работает (и мне даже не нужно превращать его в строку; я могу написать байт в файл с FileOutputStream fos = new FileOutputStream("test.txt"); fos.write(bytes); fos.close();), поэтому я доволен этим.

Код:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;

public class BytesCharsetTest {
    public static void main(String[] args) {
        float max = 1.0f;
        System.out.println("Checking " + max);
        int convInt = Float.floatToRawIntBits(max);
        byte[] bytes = ByteBuffer.allocate(4).putInt(convInt).array();

        String charset = "windows-1252";
        try {
            String stringForFile = new String(bytes, Charset.forName(charset));
            System.out.println(stringForFile);

            Writer output = Files.newBufferedWriter(Paths.get("test.txt"), Charset.forName(charset));
            output.write(stringForFile);
            output.close();
        } catch (IOException e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
    }
}

Вывод на консоль:

Checking 1.0
?€  

Process finished with exit code 0
...