Java 8 UTF-16 не является кодировкой по умолчанию, а UTF-8

Я немного кодировал с помощью String в Java8, Java 11, но этот вопрос основан на Java 8. У меня есть этот небольшой фрагмент.

final char e = (char)200;//È

Я просто думал, что символы между 0,255 [Ascii + расширенный Ascii] всегда будут помещаться в байт только потому, что 2 ^ 8 = 256, но это не так, я попробовал на веб-сайте https://mothereff.in/byte-counter и указывает, что символ занимает 2 байта, может кто-нибудь объяснить мне.

Другой вопрос во многих сообщениях гласит, что Java - это UTF-16, но на моей машине под управлением Windows 7 в этом фрагменте возвращается UTF-8.

String csn = Charset.defaultCharset().name();

Зависит ли эта платформа?

Другие вопросы, которые у меня есть, попробуйте этот фрагмент.

final List<Charset>charsets = Arrays.asList(StandardCharsets.ISO_8859_1,StandardCharsets.US_ASCII,StandardCharsets.UTF_16,StandardCharsets.UTF_8);
    charsets.forEach(a->print(a,"È"));
    System.out.println("getBytes");
    System.out.println(Arrays.toString("È".getBytes()));
    charsets.forEach(a->System.out.println(a+" "+Arrays.toString(sb.toString().getBytes(a))));

private void print(final Charset set,final CharSequence sb){
    byte[] array = new byte[4];              
    set.newEncoder()
            .encode(CharBuffer.wrap(sb), ByteBuffer.wrap(array), true);
    final String buildedString = new String(array,set);
    System.out.println(set+" "+Arrays.toString(array)+" "+buildedString+"<<>>"+buildedString.length());    
}

И печатает

run:
ISO-8859-1 [-56, 0, 0, 0] È//PERFECT USING 1 BYTE WHICH IS -56
US-ASCII [0, 0, 0, 0] //DONT GET IT SEE THIS ITEM FOR LATER
UTF-16 [-2, -1, 0, -56] È<<>>1 //WHAT IS -2,-1 BYTE USED FOR? I HAVE TRY WITH OTHER EXAMPLES AND THEY ALWAYS APPEAR AM I LOSING TWO BYTES HERE??
UTF-8 [-61, -120, 0, 0] 2 È //SEEMS TO MY CHARACTER NEEDS TWO BYTES?? I THOUGHT THAT CODE=200 WOULD REQUIRE ONLY ONE
getBytes
[-61, -120]//OK MY UTF-8 REPRESENTATION
ISO-8859-1 [-56]//OK
US-ASCII [63]//OK BUT WHY WHEN I ENCODE IN ASCCI DOESNT GET ANY BYTE ENCODED?
UTF-16 [-2, -1, 0, -56]//AGAIN WHAT ARE -2,-1 IN THE LEADING BYTES?
UTF-8 [-61, -120]//OK

я пробовал

System.out.println(new String(new byte[]{-1,-2},"UTF-16"));//SIMPLE "" I AM WASTING THIS 2 BYTES??

В резюме.

  1. Почему UTF-16 всегда имеет два начальных байта, они тратятся впустую? новый байт[]{-1,-2}

  2. Почему, когда я кодирую «È», я не получаю никаких байтов в кодировке ASCCI, но когда я делаю È.getBytes(StandardCharsets.US_ASCII), я получаю {63}?

  3. Java использует UTF-16, но в моем случае UTF-8 зависит от платформы?

Извините, если этот пост сбивает с толку

Окружающая обстановка

Windows 7 64 Bits Netbeans 8.2 with Java 1.8.0_121

person chiperortiz    schedule 10.03.2019    source источник


Ответы (2)


Первый вопрос

Для вашего первого вопроса: эти байты являются кодом спецификации, и они определяют порядок байтов (наименьший или самый значащий идет первым) многобайтовой кодировки, такой как UTF-16.

Второй вопрос

Каждый символ ASCII может быть закодирован как один байт в UTF-8. Но ASCII не является 8-битной кодировкой, она использует 7 бит для каждого символа. И на самом деле, для всех символов Unicode с кодовыми точками >= 128 требуется как минимум два байта. (Причина в том, что вам нужен способ различать 200 и многобайтовую кодовую точку, первый байт которой равен 200. UTF-8 решает эту проблему, используя байты >= 128 для представления многобайтовых кодовых точек.)

'È' не является символом ASCII, поэтому он не может быть представлен в ASCII. Это объясняет второй вывод: 63 — это ASCII для символа '?'. И действительно, Javadoc для метода getBytes(Charset) указывает, что неотображаемый ввод сопоставляется с «массивом байтов замены по умолчанию», в данном случае «?». С другой стороны, чтобы получить первый массив байтов ASCII, вы использовали CharsetEncoder напрямую, что является более низкоуровневым API и не выполняет таких автоматических замен. (Если бы вы проверили результат метода encode, вы бы обнаружили, что он вернул экземпляр CoderResult, представляющий ошибку.)

Третий вопрос

Java 8 Strings использует UTF-16 внутри, но при взаимодействии с другим программным обеспечением могут ожидаться другие кодировки, например UTF-8. Метод Charset.defaultCharset() возвращает набор символов виртуальной машины по умолчанию, который зависит от локали и набора символов операционной системы, а не от кодировки, используемой внутри строк Java.

person Hoopje    schedule 10.03.2019
comment
ОС не зависит от текущего выбора пользователем кодировки символов. Windows API использует кодировку UTF-16; Файловая система использует UTF-16, система GUI использует UTF-16. Все, что он делает, это позволяет пользователю выбрать один и сделать этот выбор доступным для программ. Им решать, отражает ли этот выбор намерения пользователя о том, как кодировать/декодировать текстовые файлы. Конечно, некоторые такие программы предоставляются операционной системой, например cmd.exe. Notepad.exe переворачивает его и предлагает обратно пользователю вместе с UTF-8, UTF-16LE и UTF-16BE. И старые программы, такие как javac, тоже используют его, если не указано иное. - person Tom Blodget; 10.03.2019
comment
@ Том Блоджет. Спасибо за ваш комментарий. Я перефразировал абзац. - person Hoopje; 11.03.2019
comment
спасибо, как я могу создать исключение, когда символ не разрешен? и как можно интерпретировать спецификацию {-1,-2}, я имею в виду, имеет ли особое значение шаблон, чтобы Java распознал его как спецификацию? - person chiperortiz; 13.03.2019

Немного отступим…

Текстовые типы данных Java используют кодировку символов UTF-16 набора символов Unicode. (Как и VB4/5/6/A/Script, JavaScript, .NET, ….) Вы можете увидеть это в различных операциях, которые вы выполняете со строковым API: индексация, длина, ….

Библиотеки поддерживают преобразование между текстовыми типами данных и массивами байтов с использованием различных кодировок. Некоторые из них относятся к категории «Расширенный ASCII», но заявляют, что это очень плохая замена именованию фактически используемой кодировки символов.

Некоторые операционные системы позволяют пользователю назначать кодировку символов по умолчанию. (Большинство пользователей, однако, не знают или не заботятся об этом.) Java пытается понять это. Это полезно только тогда, когда программа понимает, что ввод от пользователя - это та кодировка символов или то, что должно быть на выходе. В этом столетии пользователи, работающие с текстовыми файлами, предпочитают использовать определенную кодировку, передавать их без изменений в разных системах, не ценят преобразования с потерями и, следовательно, не используют эту концепцию. С точки зрения программы, это никогда не то, что вы хотите, если это не то, что вы хотите.

Там, где преобразование будет с потерями, у вас есть выбор: заменить символ (например, '?'), опустить его или создать исключение.

Кодировка символов — это сопоставление между кодовой точкой (целым числом) набора символов и одной или несколькими кодовыми единицами в соответствии с определением кодировки. Кодовая единица имеет фиксированный размер, и количество кодовых единиц, необходимых для кодовой точки, может варьироваться в зависимости от кодовой точки.

В библиотеках, как правило, бесполезно иметь массив единиц кода, поэтому они выполняют дальнейший шаг преобразования в/из массива байтов. Однако значения byte находятся в диапазоне от -128 до 127, это интерпретация Java как 8-битные целые числа с дополнением до двух. Поскольку байты понимаются как кодировка текста, значения будут интерпретироваться в соответствии с правилами кодировки символов.

Поскольку некоторые кодировки Unicode имеют кодовые единицы длиной более одного байта, порядок байтов становится важным. Итак, на уровне массива байтов есть UTF-16 Big Endian и UTF-16 Little Endian. При передаче текстового файла или потока вы должны отправлять байты, а также иметь общие знания о кодировке. Эти «метаданные» необходимы для понимания. Так, например, UTF-16BE или UTF-16LE. Чтобы сделать это немного проще, Unicode позволяет некоторым метаданным в начале файла или потока указывать порядок байтов. Это называется меткой порядка байтов (BOM). Таким образом, внешние метаданные могут иметь общую кодировку (скажем, UTF-16), а внутренние метаданные — общий порядок байтов. Юникод допускает присутствие спецификации, даже если порядок байтов не имеет значения, например UTF-8. Итак, если понимание состоит в том, что байты представляют собой текст, закодированный с помощью любой кодировки Unicode, и присутствует спецификация, то очень просто выяснить, какая это кодировка Unicode и каков порядок байтов, если это применимо.

1) Вы видите спецификацию в некоторых выходных данных кодировки Unicode.

2) È не входит в набор символов ASCII. Что бы хотелось, чтобы произошло в этом случае? Я часто предпочитаю исключение.

3) Система, которую вы использовали для своей учетной записи во время ваших тестов, могла иметь UTF-8 в качестве кодировки символов по умолчанию. Важно ли это для того, как вы хотите, и кодировать ли ваши текстовые файлы в этой системе?

person Tom Blodget    schedule 10.03.2019
comment
спасибо, как я могу создать исключение, когда символ не разрешен? - person chiperortiz; 13.03.2019