2017-02-03 2 views
3

Я хочу преобразовать десятичное значение в ascii, и это код возвращает неожиданные результаты. Вот код, который я использую.Как преобразовать один символ в символ `char`, заданный набором символов?

public static void main(String[] args) { 
    char ret= (char)146; 
    System.out.println(ret);// returns nothing. 

Я ожидаю, чтобы получить символ одного «'», как на http://www.ascii-code.com/ Любой сталкивался с этим? Благодарю.

+4

"Extended ASCII" является неправильным произведением. ASCII по определению имеет значение до 127. Есть наборы символов, которые расширяют этот диапазон, но как они это делают, это дико меняется. Поэтому вам действительно нужно знать, о чем вы говорите. Что вы делаете в своем коде, печатает кодовый номер Unicode 146, который совпадает с одной верхней цитатой, к счастью: http://www.fileformat.info/info/unicode/char/92/index.htm –

+0

Возможно, ваш ответ здесь : http://stackoverflow.com/questions/13012871/converting-ascii-code-to-char-in-java –

+0

на моем входе У меня есть десятичные числа, превышающие 127. Некоторые из них преобразуются правильно, но некоторые вроде 146 приносят неприятности. – Paresh

ответ

6

Итак, несколько вещей.

Прежде всего странице вы связаны говорит, что это о диапазоне точки кода в вопросе:

выдвинутом Коды ASCII (код символа 128-255)

Есть несколько различных вариантов 8-разрядной таблицы ASCII. Нижеследующая таблица соответствует ISO 8859-1, также называемому ISO Latin-1. Коды 128-159 содержат расширенные символы Microsoft® Windows Latin-1.

Это неверно, или, по крайней мере, для меня, вводящим в заблуждение. ISO 8859-1/Latin-1 does not define code point 146another reference just because). Так что это уже просит неприятностей. Вы можете увидеть это также, если вы делаете преобразование через String:

String s = new String(new byte[] {(byte)146}, "iso-8859-1"); 
System.out.println(s); 

Выходы же «неожиданный» результат. Это появляется, что они на самом деле ссылаются на набор Windows-1252 (он же «Windows Latin-1», но это имя почти полностью устарело в эти дни), который определяет эту кодовую точку как правую одиночную кавычку (для других кодировок которые обеспечивают этот символ на 146 см this list и искать кодировки, которые обеспечивают его на 0x92), и мы можем убедиться в этом, как, например:

String s = new String(new byte[] {(byte)146}, "windows-1252"); 
System.out.println(s); 

так первая ошибка состоит в том, что страница запутанная.

Но большая ошибка заключается в том, что вы не можете делать то, что вы пытаетесь сделать так, как вы это делаете. A char в Java - это кодовая точка UTF-16 (или половина из них, если вы представляете дополнительные символы> 0xFFFF, один char соответствует точке BMP, пара из них или int соответствует всему диапазону, включая дополнительные).

К сожалению, Java действительно не предоставляет много API для односимвольных преобразований. Даже Character не имеет доступных способов конвертировать из кодировки по вашему выбору в UTF-16.

Таким образом, один из вариантов заключается в том, чтобы сделать это через String, как указано в приведенных выше примерах, например.выражать свои кодовые точки в качестве исходного массива byte[] и конвертировать оттуда:

String s = new String(new byte[] {(byte)146}, "windows-1252"); 
System.out.println(s); 
char c = s.charAt(0); 
System.out.println(c); 

Вы могли захватить char снова через s.charAt(0). Обратите внимание, что при этом вы должны помнить о своем наборе символов. Здесь мы знаем, что наша байтовая последовательность действительна для указанной кодировки, и мы знаем, что результат остается только одним , поэтому мы можем это сделать.

Однако вы должны следить за вещами в общем случае. Например, возможно, ваша последовательность байтов и набор символов дают результат, который находится в дополнительном диапазоне символов UTF-16. В этом случае s.charAt(0) будет недостаточным, и вместо этого потребуется s.codePointAt(0), сохраненный в int.

В качестве альтернативы, с теми же оговорками, можно использовать Charset для декодирования, хотя это так же, как неуклюжий, например:

Charset cs = Charset.forName("windows-1252"); 
CharBuffer cb = cs.decode(ByteBuffer.wrap(new byte[] {(byte)146})); 
char c = cb.get(0); 
System.out.println(c); 

Обратите внимание, что я не совсем уверен, как Charset#decode обрабатывает дополнительные символы и может» t действительно тестируйте прямо сейчас (но кто-нибудь, не стесняйтесь звонить).


Как и в сторону: В вашем случае, 146 (0x92) отливать непосредственно char соответствует UTF-16 символ «PRIVATE USE ДВА» (see also), и все ставки выключены для того, что вы будете в конечном показывая там. Этот символ равен classified by Unicode as a control character и, кажется, попадает в диапазон символов, зарезервированных для управления терминалом ANSI (хотя AFAIK фактически не используется, но он находится в этом диапазоне независимо). Я бы не удивился, если бы браузеры в некоторых локалях отображали его как правую одиночную кавычку для совместимости, но терминалы сделали с ней что-то странное.

Кроме того, fyi, официальный код UTF-16 для right single quote is 0x2019. Вы можете надежно хранить, что в char, используя это значение, например:

System.out.println((char)0x2019); 

Вы также можете увидеть это для себя, глядя на значение после преобразования из окон-1252:

String s = new String(new byte[] {(byte)146}, "windows-1252"); 
char c = s.charAt(0); 
System.out.printf("0x%x\n", (int)c); // outputs 0x2019 

Или , для полноты:

String s = new String(new byte[] {(byte)146}, "windows-1252"); 
int cp = s.codePointAt(0); 
System.out.printf("0x%x\n", cp); // outputs 0x2019 
+0

Чтение этого информационного сообщения подняло вопрос, на мой взгляд, почему язык позволяет использовать (char) тип вообще, или, если он это делает, почему он не требует или, по крайней мере, позволяет, кодирование должно быть указано вместе с ним, например (char: cp1252) integer_variable или что-то еще. Существуют десятки однобайтовых схем кодирования: https://en.wikipedia.org/wiki/Windows_code_page –

+0

@Thomas Ну, это позволяет делать актеры, потому что нет причин не делать этого, и это должен быть странный частный случай , так как отбрасывание между совместимыми типами разрешено во всех других случаях. Это было бы излишне ограничивающим и ценой реализации ошибки компилятора специального случая. –

+0

Что касается второго вопроса, подразумевается кодирование: char - UTF-16. Период. Конечно, было бы удобно *, если бы вы могли указать кодировку и укомплектовать компилятор для поиска в Charset для вас, но это будет просто сахар. А также это означает, что для некоторых бросков потребуется обработка исключений Charset.forName может вызывать (https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html#forName(java. lang.String)). Также я чувствую, что кодировка с одним символом является относительно редким случаем использования, следовательно, здесь слабый API. Я бы предпочел увидеть конструктор символов, который принимает имя Charset. –

0

На странице, на которую вы ссылаетесь, упоминается, что значения от 160 до 255 соответствуют таблице ISO-8859-1 (aka Latin 1); так как для значений в диапазоне от 128 до 159 они относятся к конкретному варианту Windows Latin 1 (ISO-8859-1 оставляют этот диапазон неопределенным, назначаемым операционной системой).

Java-символы основаны на UTF16, который сам основан на таблице Unicode. Если вы хотите конкретно ссылаться на правый символ кавычки, вы можете указать его как '\u2019' в Java (см. http://www.fileformat.info/info/unicode/char/2019/index.htm).

Смежные вопросы