Java кодировка windows 1251 в utf 8

Scanner sc = new Scanner(System.in); System.out.println("Enter text: "); String text = sc.nextLine(); try { String result = new String(text.getBytes("windows-1251"), Charset.for...

First java text, String/char/Reader/Writer is internally Unicode, so it can combine all scripts.
This is a major difference with for instance C/C++ where there is no such standard.

Now System.in is an InputStream for historical reasons. That needs an indication of encoding used.

Scanner sc = new Scanner(System.in, "Windows-1251");

The above explicitly sets the conversion for System.in to Cyrillic. Without this optional parameter the default encoding is taken. If that was not changed by the software, it would be the platform encoding. So this might have been correct too.

Now text is correct, containing the Cyrillic from System.in as Unicode.

You would get the UTF-8 bytes as:

byte[] bytes = text.getBytes(StandardCharsets.UTF_8);

The old «recoding» of text was wrong; drop this line. in fact not all Windows-1251 bytes are valid UTF-8 multi-byte sequences.

String result = text;

System.out.println(result);

System.out is a PrintStream, a rather rarely used historic class. It prints using the default platform encoding. More or less rely on it, that the default encoding is correct.

System.out.println(result);

For printing to an UTF-8 encoded file:

byte[] bytes = ("uFEFF" + text).getBytes(StandardCharsets.UTF_8);
Path path = Paths.get("C:/Temp/test.txt");
Files.writeAllBytes(path, bytes);

Here I have added a Unicode BOM character in front, so Windows Notepad may recognize the encoding as UTF-8. In general one should evade using a BOM. It is a zero-width space (=invisible) and plays havoc with all kind of formats: CSV, XML, file concatenation, cut-copy-paste.


This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters

Show hidden characters

public class Encodings
{
public static void main(String[] args) throws IOException
{
FileInputStream inputStream = new FileInputStream(«d:/data.txt»);
FileOutputStream outputStream = new FileOutputStream(«d:/data.txt»);
SortedMap<String, Charset> charsets = Charset.availableCharsets();//список доступных кодировок
Charset currentCharset = Charset.defaultCharset();//узнать текущую кодировку
String s = «Good news everyone!»;
byte[] buffer = s.getBytes(«Windows-1251»);//создать массив байт в любой известной Java кодировке
byte[] fileBuffer = new byte[1000];
inputStream.read(fileBuffer);
String s1 = new String(fileBuffer, «Windows-1251»);//преобразовать набор байт, прочитанных из файла в строку
//преобразовать набор байт из одной кодировки в другую
Charset koi8 = Charset.forName(«KOI8-R»);
Charset windows1251 = Charset.forName(«Windows-1251»);
byte[] buffer3 = new byte[1000];
inputStream.read(buffer3);
String s3 = new String(buffer3, koi8);
buffer3 = s3.getBytes(windows1251);
outputStream.write(buffer3);
}
}

1. Введение

Некоторые проблемы настолько сложны,
что нужно быть очень умным и очень хорошо
информированным, чтобы не быть уверенным
в их решении.

 

Лоренс Дж. Питер
Peter’s Almanac

Кодировки

   Когда я только начинал программировать на языке C, первой моей программой (не считая HelloWorld) была программа перекодировки текстовых файлов из основной кодировки ГОСТ-а (помните такую? :-) в альтернативную.
Было это в далёком 1991-ом году.
С тех пор многое изменилось, но за прошедшие 10 лет подобные программки свою актуальность, к сожалению, не потеряли.
Слишком много уже накоплено данных в разнообразных кодировках и слишком много используется программ, которые умеют работать только с одной.
Для русского языка существует не менее десятка различных кодировок, что делает проблему ещё более запутанной.

   Откуда же взялись все эти кодировки и для чего они нужны?
Компьютеры по своей природе могут работать только с числами.
Для того чтобы хранить буквы в памяти компьютера надо поставить в соответствие каждой букве некое число (примерно такой же принцип использовался и до появления компьютеров — вспомните про ту же азбуку Морзе).
Причём число желательно поменьше — чем меньше двоичных разрядов будет задействовано, тем эффективнее можно будет использовать память.
Вот это соответствие набора символов и чисел собственно и есть кодировка.
Желание любой ценой сэкономить память, а так же разобщённость разных групп компьютерщиков и привела к нынешнему положению дел.
Самым распространённым способом кодирования сейчас является использование для одного символа одного байта (8 бит), что определяет общее кол-во символов в 256.
Набор первых 128 символов стандартизован (набор ASCII) и является одинаковыми во всех распространённых кодировках (те кодировки, где это не так уже практически вышли из употребления).
Англицкие буковки и символы пунктуации находятся в этом диапазоне, что и определяет их поразительную живучесть в компьютерных системах :-).
Другие языки находятся не в столь счастливом положении — им всем приходится ютиться в оставшихся 128 числах.

Unicode

   В конце 80-х многие осознали необходимость создания единого стандарта на кодирование символов, что и привело к появлению Unicode.
Unicode — это попытка раз и навсегда зафиксировать конкретное число за конкретным символом.
Понятно, что в 256 символов тут не уложишься при всём желании.
Довольно долгое время казалось, что уж 2-х то байт (65536 символов) должно хватить.
Ан нет — последняя версия стандарта Unicode — 3.1 определяет уже 94140 символов.
Для такого количества символов, наверное, уже придётся использовать 4 байта (4294967296 символов).
Может быть и хватит на некоторое время… :-)

   В набор символов Unicode входят всевозможные буквы со всякими чёрточками и припендюльками, греческие, математические, иероглифы, символы псевдографики и пр. и пр.
В том числе и так любимые нами символы кириллицы (диапазон значений 0x0400-0x04ff).
Так что с этой стороны никакой дискриминации нет.

   Если Вам интересны конкретные кода символов, для их просмотра удобно использовать программу «Таблица символов» из WinNT.
Вот, например, диапазон кириллицы:

Таблица символов Unicode

   Если у Вас другая OS или Вас интересует официальное толкование, то полную раскладку символов (charts) можно найти на официальном сайте Unicode.

Типы char и byte

   В Java для символов выделен отдельный тип данных char размером в 2 байта.
Это часто порождает путаницу в умах начинающих (особенно если они раньше программировали на других языках, например на C/C++).
Дело в том, что в большинстве других языков для обработки символов используются типы данных размером в 1 байт.
Например, в C/C++ тип char в большинстве случаев используется как для обработки символов, так и для обработки байтов — там нет разделения.
В Java для байтов имеется свой тип — тип byte.
Таким образом C-ишному char соответствует Java-вский byte, а Java-вскому char из мира C ближе всего тип wchar_t.
Надо чётко разделять понятия символов и байтов — иначе непонимание и проблемы гарантированны.

   Java практически с самого своего рождения использует для кодирования символов стандарт Unicode.
Библиотечные функции Java ожидают увидеть в переменных типа char символы, представленные кодами Unicode.
В принципе, Вы, конечно, можете запихнуть туда что угодно — цифры есть цифры, процессор всё стерпит, но при любой обработке библиотечные функции будут действовать исходя из предположения что им передали кодировку Unicode.
Так что можно спокойно полагать, что у типа char кодировка зафиксирована.
Но это внутри JVM.
Когда данные читаются извне или передаются наружу, то они могут быть представлены только одним типом — типом byte.
Все прочие типы конструируются из байтов в зависимости от используемого формата данных.
Вот тут то на сцену и выходят кодировки — в Java это просто формат данных для передачи символов, который используется для формирования данных типа char.
Для каждой кодовой страницы в библиотеке имеется по 2 класса перекодировки (ByteToChar и CharToByte).
Классы эти лежат в пакете sun.io.
Если, при перекодировке из char в byte не было найдено соответствующего символа, он заменяется на символ «?«.

   Кстати, эти файлы кодовых страниц в некоторых ранних версиях JDK 1.1 содержат ошибки, вызывающие ошибки перекодировок, а то и вообще исключения при выполнении.
Например, это касается кодировки KOI8_R.
Лучшее, что можно при этом сделать — сменить версию на более позднюю.
Судя по Sun-овскому описанию, большинство этих проблем было решено в версии JDK 1.1.6.

   До появления версии JDK 1.4 набор доступных кодировок определялся только производителем JDK.
Начиная с 1.4 появилось новое API (пакет java.nio.charset), при помощи которого Вы уже можете создать свою собственную кодировку (например поддержать редко используемую, но жутко необходимую именно Вам).

Класс String

   В большинстве случаев для представления строк в Java используется объект типа java.lang.String.
Это обычный класс, который внутри себя хранит массив символов (char[]), и который содержит много полезных методов для манипуляции символами.
Самые интересные — это конструкторы, имеющие первым параметром массив байтов (byte[]) и методы getBytes().
При помощи этих методов Вы можете выполнять преобразования из массива байтов в строки и обратно.
Для того, чтобы указать какую кодировку при этом использовать у этих методов есть строковый параметр, который задаёт её имя.
Вот, например, как можно выполнить перекодировку байтов из КОИ-8 в Windows-1251:

	//Данные в кодировке КОИ-8
	byte[] koi8Data=...;
	//Преобразуем из КОИ-8 в Unicode
	String string=new String(koi8Data,"KOI8_R");
	//Преобразуем из Unicode в Windows-1251
	byte[] winData=string.getBytes("Cp1251");

   Список 8-ми битовых кодировок, доступных в современных JDK и поддерживающих русские буквы Вы можете найти ниже, в разделе «8-ми битовые кодировки русских букв».

   Так как кодировка — это формат данных для символов, кроме знакомых 8-ми битовых кодировок в Java также на равных присутствуют и многобайтовые кодировки.
К таким относятся UTF-8, UTF-16, Unicode и пр.
Например вот так можно получить байты в формате UnicodeLittleUnmarked (16-ти битовое кодирование Unicode, младший байт первый, без признака порядка байтов):

	//Строка Unicode
	String string="...";
	//Преобразуем из Unicode в UnicodeLittleUnmarked
	byte[] data=string.getBytes("UnicodeLittleUnmarked");

   При подобных преобразованиях легко ошибиться — если кодировка байтовых данных не соответствуют указанному параметру при преобразовании из byte в char, то перекодирование будет выполнено неправильно.
Иногда после этого можно вытащить правильные символы, но чаще всего часть данных будет безвозвратно потеряна.

   В реальной программе явно указывать кодовую страницу не всегда удобно (хотя более надёжно).
Для этого была введена кодировка по умолчанию.
По умолчанию она зависит от системы и её настроек (для русских виндов принята кодировка Cp1251), и в старых JDK её можно изменить установкой системного свойства file.encoding.
В JDK 1.3 изменение этой настройки иногда срабатывает, иногда — нет.
Вызвано это следующим: первоначально file.encoding ставится по региональным настройкам компьютера.
Ссылка на кодировку по умолчанию запоминается в нутрях при первом преобразовании.
При этом используется file.encoding, но это преобразование происходит ещё до использования аргументов запуска JVM (собсно, при их разборе).
Вообще-то, как утверждают в Sun, это свойство отражает системную кодировку, и она не должна изменяться в командной строке (см., например, комментарии к BugID 4163515).
Тем не менее в JDK 1.4 Beta 2 смена этой настройки опять начала оказывать эффект.
Что это, сознательное изменение или побочный эффект, который может опять исчезнуть — Sun-овцы ясного ответа пока не дали.

   Эта кодировка используется тогда, когда явно не указанно название страницы.
Об этом надо всегда помнить — Java не будет пытаться предсказать кодировку байтов, которые Вы передаёте для создания строки String (так же она не сможет прочитать Ваши мысли по этому поводу :-).
Она просто использует текущую кодировку по умолчанию.
Т.к. эта настройка одна на все преобразования, иногда можно наткнуться на неприятности.

   Для преобразования из байтов в символы и обратно следует пользоваться только этими методами.
Простое приведение типа использовать в большинстве случаев нельзя — кодировка символов при этом не будет учитываться.
Например, одной из самых распространённых ошибок является чтение данных побайтно при помощи метода read() из InputStream, а затем приведение полученного значения к типу char:

	InputStream is=...;
	int b;
	StringBuffer sb=new StringBuffer();
	while((b=is.read())!=-1){
		sb.append((char)b); // так делать нельзя
	}
	String s=sb.toString();

   Обратите внимание на приведение типа — «(char)b».
Значения байтов вместо перекодирования просто скопируются в char (диапазон значений 0-0xFF, а не тот, где находится кириллица).
Такому копированию соответствует кодировка ISO-8859-1 (которая один в один соответствует первым 256 значениям Unicode), а значит, можно считать, что этот код просто использует её (вместо той, в которой реально закодированы символы в оригинальных данных).
Если Вы попытаетесь отобразить полученное значение — на экране будут или вопросики или кракозяблы.
Например, при чтении строки «АБВ» в виндовой кодировке может запросто отобразиться что-то вроде такого: «»ÀÁ».
Подобного рода код часто пишут программисты на западе — с английскими буквами работает, и ладно.
Исправить такой код легко — надо просто заменить StringBuffer на ByteArrayOutputStream:

	InputStream is=...;
	int b;
	ByteArrayOutputStream baos=new ByteArrayOutputStream();
	while((b=is.read())!=-1){
		baos.write(b);
	}
	//Перекодирование байтов в строку с использованием 
	//кодировки по умолчанию
	String s=baos.toString();
	//Если нужна конкретная кодировка - просто укажите 
	//её при вызове toString():
	//
	//s=baos.toString("Cp1251");

   Более подробно о распространённых ошибках смотрите раздел «Типичные ошибки».

С кодировками в java плохо. Т.е., наоборот, все идеально хорошо: внутреннее представление строк – Utf16-BE (и поддержка Unicode была с самых первых дней). Все возможные функции умеют преобразовывать строку из маленького регистра в большой, проверять является ли данный символ буквой или цифрой, выполнять поиск в строке (в том числе с регулярными выражениями) и прочее и прочее. Для этих операций не нужно использовать какие-то посторонние библиотеки вроде привычных для php mbstring или iconv. Как говорится, поддержка многоязычных тестов “есть в коробке”. Так откуда берутся проблемы? Проблемы возникают, как только строки текста пытаются “выбраться” из jvm (операции вывода текста различным потребителям) или наоборот пытаются в эту самую jvm “залезть” (операция чтения данных от некоторого поставщика).

Сказка про капиталистов

Надо сказать, что unicode это не статическое образование, не принятый еще при царе горохе стандарт, который с тех пор безуспешно пытаются реализовать производители различных продуктов. Это динамический, постоянно развивающийся стандарт, с множество версий и соответствующих спецификаций. Полагаю, с тем, что для хранения текста написанного на различных языках (а для азиатов хватит и одного их родного языка) размера символа в один байт совершенно не достаточно, согласны все. Нет, чтобы взять и прикинуть, сколько там всего языков на всей планете, сколько в их алфавитах символов, сколько разных значков (нотных, графических) может потребоваться на ближайшие 100 лет. Взяли, прикинули: 1 байт – смешно, 2 уже лучше, но все равно маловато, 3 байта (примерно 16 миллионов символов уже хорошо), а если взять для представления символа все 4 байта (4 миллиарда с гаком) – то просто замечательно. Приняли бы такое решение, потом бы издали указ: мол, так и так с первого числа сего месяца начинается новые и улучшенные компьютерные времена, переделали бы все заводы по производству компьютеров, вызывали Била Гейтса на партсобрание, дали бы ценное указание и жизнь стала бы гораздо лучше. Увы, в этом жестком капиталистическом мире, на всей планете найдется всего несколько человек, которые согласятся ради возможности решить раз и навсегда все проблемы на то, чтобы объем их винчестеров и оперативной памяти уменьшится сразу в четыре раза (во сколько раз уменьшится производительность вычислений сказать тяжело – но все равно очень неслабо). Да, еще достижение всеобщего блага привело бы к полной потере всего ранее написанного софта, документов и т.д. Увы капитализм не захотел устроить всемирный субботник, а нам наследникам красного октября приходится это расхлебывать. Конечно, это шутка. Но во всякой шутке, как известно, есть доля шутки.
Первая версия Unicode представляла собой кодировку, в которой каждый символ кодировался 2 байтами, для некоторых символов (наиболее часто используемых, а не всех возможных) были выделены определенные области (интервалы). Потом, с течением времени решили, что все же 64 тысяч символов будет маловато и необходим механизм хранения их большего числа. Кроме того, разработчики стандарта поняли, что в разных странах разное понятие о “букве” и все стало еще сложнее. В принципе все это написано на wikipedia, так что прекращаю рассказывать сказки, и перехожу к java, точнее к проблемам связанным с кодировками в java.

Типовые проблемы с которыми сталкиваются java-разработчики

Т.к. java приложения взаимодействуют с различными подсистемами, то и возникающие проблемы бывают разными. Хотя все, в общем случае, сводится к одной из двух проблем:

Проблема 1. Данные были успешно прочитаны, но на стадии отображения не нашлись нужные шрифты. В этом случае отсутствующие картинки шрифта заменяются на квадратики. Лечится проблема путем установки нужных шрифтов (например, при установке windows вас обычно спрашивают, хотите ли вы добавить поддержку шрифтов для азиатских языков). Есть два вида шрифтов: физические и логические. Физические шрифты – это те шрифты, файлы которых установлены либо в папку там_где_ваша_jre/lib/fonts, либо те шрифты которые установлены в стандартное место для вашей операционной системы (все версии jre обязаны поддерживать шрифты TrueType, остальные же форматы — необязательно). Логические шрифты (например, Serif, Sans-Serif, Monospaced, Dialog и DialogInput) – это правила отображения некоторых имен на реальные физические шрифты. Например, для windows логический шрифт serif это ссылка на физический times new roman. Задаются эти правила в файлах fontconfig.properties.src, fontconfig.98.properties.src, fontconfig.Me.properties.src. Для swing приложений, мы можем не только работать с идущими в самой операционной системе шрифтами, но и носить файл шрифта вместе со своим приложением, так чтобы полностью не зависеть от того, где оно будет запущено.
В составе класса java.awt.GraphicsEnvironment есть несколько методов позволяющих получить информацию о том, какие шрифты доступны на вашем компьютере.

public Font[] getAllFonts()
public abstract String[] getAvailableFontFamilyNames()

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

Для того, чтобы создать шрифт на основании некоторого файла ttf, необходимо вызвать статический метод createFont из класса Font. В качестве параметров для него следует указать файл, который содержит определение шрифта, а также указать тип этого файла (Font.TRUETYPE_FONT или Font.TYPE1_FONT). Созданный объект шрифта можно “настроить” указав для него размер или стиль (plain, italic, bold). Используйте для этого метод deriveFont.

Изображение:font_1_1.png

final JFrame jf = new JFrame("barra");
jf.setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE);

JPanel pa =

new JPanel(new GridLayout(0, 1));

JLabel lab_1 =

new JLabel("Гравитационные волны");
JLabel lab_2 =
new JLabel("Гравитационные волны");
JLabel lab_3 =
new JLabel("Гравитационные волны");
JLabel lab_4 =
new JLabel("Гравитационные волны");
// используем стандартные шрифты: в первом случае логический шрифт, а во
// втором физический.
// обратите внимание, что на картинке они выглядят одинаково
lab_1.setFont(new Font("Serif", Font.PLAIN, 24));
lab_2.setFont
(new Font("Times New Roman", Font.PLAIN, 24));// теперь пробуем загрузить шрифт из внешнего файла
Font f_ye = Font.createFont(Font.TRUETYPE_FONT, new File("yermak.ttf"));
lab_3.setFont
(f_ye.deriveFont(Font.PLAIN, 24.0f));
// и еще один шрифт из внешнего файла
Font f_inv = Font.createFont(Font.TRUETYPE_FONT,new File("invest.ttf"));
lab_4.setFont
(f_inv.deriveFont(Font.PLAIN, 24.0f));// получим и выведем в виде JComboBox список всех шрифтов
pa.add(new JLabel("getAllFonts"));
Font
[] allFonts = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
pa.add
(new JComboBox(allFonts));
pa.add
(new JLabel("count fonts = "+ allFonts.length));
// список названий всех шрифтов доступных для текущей локали
pa.add(new JLabel("getAvailableFontFamilyNames"));
String
[] locFontNames = java.awt.GraphicsEnvironment.
getLocalGraphicsEnvironment
().getAvailableFontFamilyNames();
pa.add
(new JComboBox(locFontNames));
pa.add
(new JLabel("count fonts = "+ locFontNames.length));

pa.add

(lab_1);
pa.add
(lab_2);
pa.add
(lab_3);
pa.add
(lab_4);

jf.setContentPane

(pa);
jf.pack
();

SwingUtilities.invokeLater

(
  
new  Runnable(){
     
public void run(){
        
jf.setVisible(true);
     
}
   }
)
;

Теперь вернемся к русским буквам и java.

Вторая наиболее часто встречающаяся проблема — это неправильное преобразование кодировки. Например, вы хотите прочитать текстовый файл в кодировке windows-1251. Но при создании объекта InputStreamReader вы указали неверную кодировку (или положились на значение по-умолчанию).

InputStreamReader isr = new InputStreamReader(new FileInputStream("файл_в_кодировке_1251"),"utf-8"));

В результате при чтении файла символы будут рассматриваться как принадлежащие определенной кодовой странице. Но вовсе не факт что некоторый код символа корректный для кодировки A будет также корректен для кодировки B. В случае корректности кодов, мы увидим то, что некоторые из символов были заменены на какие-то другие символы. А вот, если код является некорректным (например, зарезервирован на будущее), то такой символ будет заменен на знак “?”.

Для получения списка всех доступных кодировок вы можете использовать следующий код (вызов статического метода
Charset.availableCharsets):

SortedMap<String, Charset> charsetsMap = Charset.availableCharsets();
final JList list = new JList(charsetsMap.keySet().toArray());
final JScrollPane pane = new JScrollPane(list);
jf.getContentPane
().add(pane);
jf.pack
();

Изображение:font_1_2.png

В папке lib есть архив charsets.jar (размером в гадкие 9 мегабайт), в котором находятся классы, управляющие преобразованием из одной кодировки в другую (например, sunioByteToCharCp949.class).
Логично, что наиболее частой причиной второго вида ошибок являются не отсутствующие (неизвестные java кодировки), а то, что мы просто не знаем то, в какой из кодировок пришли данные. Эту проблему можно решать различными путями, как техническими, так и административными. И хорошо еще, если мы в состоянии повлиять на тот фрагмент кода, который выполняет чтение данных из файла (конфигурационной переменной или вызовом особого метода, вроде, setCharset …). А что, если проблемный кусок кода зашит где-то глубоко внутри сторонней библиотеки, к которой у нас нет исходников или нет специалистов, способных найти “то самое глючное место и исправить его так, чтобы не рухнуло все остальное”.
В том случае, если данные были прочитаны неверно, но мы знаем в какой кодировке они реально пришли и в какой кодировке их ошибочно прочитали. Тогда можно сделать попытку восстановить исходный набор символов. Основан алгоритм восстановления на двух последовательных преобразованиях: преобразовании “неправильной” строки в массив байтов (по правилам ошибочной кодировки), так мы получим данные до преобразования, а затем мы выполним правильное преобразование (в нужную кодировку).

String rightString= new String(badString.getBytes("windows-1251"),"utf-8");

Здесь “utf-8” правильная кодировка, а “windows-1251” – неправильная.

Но предупреждаю сразу – это плохой, очень плохой способ “починить примус”. Помните, что в ходе преобразований возможна потеря символов (из-за несовместимых кодировок). Так что если вы прочитали данные из файла в неверной кодировке, то отсутствующие символы были заменены на значки вопросов. Следовательно попытка восстановить оригинальный массив байтов будет безуспешной.

Java и web.

Web – это то самое место где сталкиваются множество людей работающих под разными версиями операционных систем использующие разные браузеры и написанный нами сайт должен работать всегда и везде.

Давайте рассмотрим, как данные поступают на вход браузеру от веб-сервера и то, как браузер отправляет информацию серверу (точнее веб-приложению исполняющемуся на нем). Есть два метода для отправки запросов от браузера к веб-серверу: get и post. Остальные методы, такие как put, delete, options, доступны при использовании ajax-вызовов, а внутри компонента XmlHttpRequest выполняется конвертация отправляемых данных в “utf-8”, что сразу решает ряд проблем с неизвестными кодировками.

Самая идеальная ситуация — когда отправка идет с помощью метода POST. В этом случае браузер кодирует данные в такой же кодировке, как и в той, что была сформирована веб-страница. За кодировку возвращаемых данных отвечают либо указанная вверху jsp-файла директива:

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>

Первая из этих опций (contentType) указывает на кодировку выходного документа, а вторая (pageEncoding) на кодировку собственно файла в котором находится код jsp-страницы.

Либо, если вы создаете сервлет, то первым шагом нужно указать выходную кодировку документа:

public class BlaBlaServlet extends HttpServlet {
   
protected void doPost(HttpServletRequest request, HttpServletResponse response)
   
throws ServletException, IOException {
       
response.setCharacterEncoding("utf-8");
       
// ну а теперь давайте работать ….
   
}

   
protected void doGet(HttpServletRequest request, HttpServletResponse response)
   
throws ServletException, IOException {
       
doPost (request, response);
   
}
}

Классно, значит, если мы сформировали страницу в кодировке utf-8, то данные из формы придут к нам в формате “utf-8”. Классно, то классно, но кто сказал, что ваш веб-сервер правильно эти данные сможет раскодировать? Теоретически, когда браузер делает запрос к серверу, то отправляется не только сведения о том какой документ хочет видеть клиент, не только данные из формы, но и сведения об браузере, об поддерживаемых кодировках, об предпочитаемых языках документа, и прочее и прочее и прочее. Может там найдется сведения о кодировке? Давайте проверим. При создании тега form вы должны указать значение не только метода отправки (GET или POST), но и значение атрибута enctype. Его возможные значения: «multipart/form-data» или «application/x-www-form-urlencoded». В первом случае форма будет способна отправлять не только текстовые данные, но и, например, файлы (кто бы мне сказал, почему sun-овцы не могли реализовать парсинг подобного запроса самостоятельно или внести в стандарт для любого servlet-контейнера, а отдали на откуп посторонним?). Рассмотрим как кодируются данные в случае «multipart/form-data»?
Ниже пример подобного запроса:

POST /test/servertest.jsp HTTP/1.1
Host: center:1001
Accept-Language: en,ru-ru;q=0.8,ru;q=0.5,en-us;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Content-Type: multipart/form-data; boundary=---------------------------265001916915724
Content-Length: 927
 
-----------------------------265001916915724
Content-Disposition: form-data; name="txt_1"
 
Гравитация
-----------------------------265001916915724
Content-Disposition: form-data; name="user[]"
 
 
-----------------------------265001916915724
Content-Disposition: form-data; name="user[]"
 
 
-----------------------------265001916915724
Content-Disposition: form-data; name="foto"; filename=""
Content-Type: application/octet-stream
 
 
-----------------------------265001916915724
Content-Disposition: form-data; name="pics[]"; filename=""
Content-Type: application/octet-stream
 
 
-----------------------------265001916915724
Content-Disposition: form-data; name="pics[]"; filename=""
Content-Type: application/octet-stream
 
 
-----------------------------265001916915724
Content-Disposition: form-data; name="btnsubmit"
 
Send
-----------------------------265001916915724--

Как видите, запрос разбит на секции с помощью некоторой уникальной комбинации символов. Итак, где в этом запросе есть указание на то, в какой кодировке пришли данные от браузера? Нигде, нет их. Может быть, кодировка указывается при запросе «application/x-www-form-urlencoded»? Ладно, вот пример еще одного запроса:

POST /test/servertest.jsp HTTP/1.1
Host: center:1001
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9b1) Gecko/2007110904 Firefox/3.0b1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en,ru-ru;q=0.8,ru;q=0.5,en-us;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 136
 
txt_1=%D0%A1%D0%BC%D0%B5%D1%80%D1%82%D0%BE%D0%BD%D0%BE%D1%81%D0%BD%D1%8B
%D1%85+%D0%B6%D1%83%D0%BA&user%5B%5D=&user%5B%5D=&btnsubmit=Send

Способ кодирования информации отличен: прежде всего, заметьте, как были переданы русские буквы. В первом случае они передаются как есть, т.е. в той кодировке в которой была сформирована и сама страница. Во втором случае буквы превратились во множество значков процента, цифр и букв. Кодировка «application/x-www-form-urlencoded» применяется также и в случае отправки данных методом GET (передается в адресной строке).

Символы, которые не могут быть отправлены по сети (все кроме латиницы, цифр и ряда знаков), предварительно кодируются с помощью алгоритма x-www-form-urlencoded. Байты кодировки заменяются на последовательности вида %XX. Вместо XX подставляются две шестнадцатеричные цифры (http://www.faqs.org/rfcs/rfc1738). Стоп. Самый главный вопрос: x-www-form-urlencoded – это кодировка или нет? Традиционно под кодировкой понимают комбинацию набора символов и схемы кодирования. Например, когда говорят utf-8, то подразумевают схему кодирования utf-8 и набор символов Unicode, ровно, как и для utf-16. А если вы слышите windows-1251, то здесь название кодировки дано по названию набора символов. Т.к. как такового отдельного алгоритма кодирования цифры, под которой в данном наборе фигурирует, например, буква “Ы” — нет. Просто 8 бит – бери и пиши их в файл как есть, без каких-либо дополнительных преобразований. Так что x-www-form-urlencoded – это не кодировка, это способ отправить те самые байты, в которые было выполнено преобразование строки текста согласно некоторой “настоящей” кодировке (точнее схеме кодирования). Следовательно, если я открываю адрес вида:
http : //Мой-сайт.ru/ящики_с_пивом

То, в зависимости от используемой кодировки, данные будут отправлены либо так:

%FF%F9%E8%EA%E8_%F1_%EF%E8%E2%EE%EC — так выглядит слово “ящики с пивом” в кодировке windows-1251

%D1%8F%D1%89%D0%B8%D0%BA%D0%B8_%D1%81_%D0%BF%D0%B8%D0%B2%D0%BE%D0%BC – а так выглядит это слово в кодировке utf-8.

Возвращаясь к анализу двух примеров запроса данных, мы нигде не видим указания на то какая кодировка используется для отправки данных. Может, у меня не правильный браузер, и какие то другие, правильные, браузеры указывают кодировку отправляемых данных? Увы, ни internet explorer 6,7 ни firefox 2,3 ни opera 9.5 не указывают сведений о кодировке.

Автоматически определить кодировку tomcat не может, а раз не может, то будет выполнять преобразование поступивших данных из кодировки (по-умолчанию) ISO8859-1. Несколько раз мне встречались в сети рекомендации делать что-то вроде:

String variableX = new String ( request.getParameter("fio").getBytes("Cp1251"));

Это очень плохой совет. Помните, что неправильные операции преобразования могут приводить к потере данных и, притом, необратимому.
Значит, нужно подсказать tomcat-у, как правильно выполнить декодирование (а иначе, без нашего указания, он такого на раскодирует … не исправить).

Когда вызывается ваш сервлет (или jsp, что суть одно и то же). То вы можете узнать в какой кодировке к вам пришли данные, например, так:

String chenc = hreq.getCharacterEncoding();

Если значение кодировки null (а оно равно этой величине почти всегда), тогда tomcat решает, что входные данные были в формате ISO8859-1 и пытается именно так выполнить парсинг строки.
Существует народное поверье, что если создать специальный сервлет-фильтр, который будет вызываться до того, как будет выполнено первое обращение к списку передаваемых параметров и установит значение правильной кодировки, то все заработает без проблем, например:

// выполнение фильтрации запроса
public void doFilter(ServletRequest req, ServletResponse resp,
   FilterChain chain
) throws ServletException, IOException
{
  
req.setCharacterEncoding("utf-8");
   chain.doFilter
(req, resp);
}

Теперь при первом же обращении к какому-либо из входных параметров:

fio = request.getParameter("fio");

Равно как и для jstl:

<c:out value="${param.txt_1}"/>

Будет выполнено раскодирование входных данных с учетом указанной вами кодировки.

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

<filter-mapping>
    <filter-name>MegaFilterToUTF8</filter-name>
    <url-pattern>/utf-8-dir/*</url-pattern>
</filter-mapping>

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

request.setCharacterEncoding("utf-8");

Или, если вы создаете jsp-файл с использованием jstl-тегов, то такую команду:

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<fmt:requestEncoding value="utf-8"/>

Однако для того, чтобы указанное значение кодировки было применено к параметрам переданным методом GET (применительно к tomcat) нужно выполнить правку конфигурационного файла server.xml и добавить для элемента Connector атpибут useBodyEncodingForURI равный значению “true”. В этом случае разбор параметров будет выполнен с такой кодировкой, которую вы установили с помощью вызова request.setCharacterEncoding(«utf-8»).

URIEncoding This specifies the character encoding used to decode the URI bytes, after %xx decoding the URL. If not specified, ISO-8859-1 will be used.
useBodyEncodingForURI This specifies if the encoding specified in contentType should be used for URI query parameters, instead of using the URIEncoding. This setting is present for compatibility with Tomcat 4.1.x, where the encoding specified in the contentType, or explicitely set using Request.setCharacterEncoding method was also used for the parameters from the URL. The default value is false.

Проще говоря, либо вы указывате явно значение кодировки для всех входных запросов с помощью параметра URIEncoding (а-га, вот как будто бы всегда и для всех приложений на этом хостинге только такая кодировка является допустимой). Либо устанавливаете вторую перменную |useBodyEncodingForURI равной значению true (по-умолчанию ее значение false).

Единственная проблема в том, что мы выполнять правку файла server.xml мы можем лишь, в случае если имеем прямой доступ к каталогу, где установлен tomcat. Согласитесь, что в случае типового виртуального хостинга мы можем управлять приложением только с помощью файлов web.xml и еще META-INF/context.xml – а это не то. Также, если ваше приложение запущено под другим веб-сервером, то вам нужно будет разбираться с его специфическим настройками.

Некоторое время назад я пытался разобраться с настройками для resin. В FAQ написано, что на разбор данных оказывают влияние следующие значения:

  1. request.getAttribute("caucho.form.character.encoding")
  2. The response.setContentType() encoding of the page.
  3. The character-encoding tag in the resin.conf. 

Тег character-encoding, может быть дочерним по отношению к следующим уровням настройки: resin, server, host-default, host, web-app-default, web-app (на уровне приложения, а значит мы можем настроить свое приложение даже на самом обычном виртуальном хостинге).

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://caucho.com/ns/resin"
         xmlns:resin="http://caucho.com/ns/resin/core">
   <character-encoding>utf-8</character-encoding>
</web-app>

Обратите внимание на схему, которая регламентирует содержимое web.xml в следующем примере (традиционная )

<web-app version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" >
   ....

То такой пример не будет работать: т.к. тег character-encoding является специфическим именно для resin.

Если же значение кодировки явно не указано, то для чтения данных используется кодировка по-умолчанию для jvm (file.encoding).
К сожалению, мои попытки запустить resin с указанием входной кодировки ничем хорошим не закончились (после установки значения кодировки в web.xml переданные рускоязычные символы превращались в черт его знает что). Так что пришлось обходиться привычным request.setCharacterEncoding (‘utf-8’); Тогда у меня было мало времени разбираться в особенностях поведения resin, также я не исключаю что это был хитрый баг, так что если у кого-то есть заметки по этому поводу, то прошу поделиться с общественностью.

Как вывод: если данные передаются методом POST, то проблем нет. Если методом GET то проблемы есть; и особенно большие проблемы в случае, если кодировки для метода GET и POST отличаются друг от друга, или некоторые GET запросы приходят в одной кодировке, а некоторые – в другой. Думаете, такого не может быть? Может. Ситуация была такова: есть сайт в кодировке utf-8 на его страницах находится множество ссылок ссылающихся на разделы этого же сайта, и, внимание, в тексте ссылок содержались русскоязычные названии (как некое подобие wikipedia). Если человек жмет на такую ссылку то адресная строка перед кодированием ее с помощью x-www-form-urlencoded, подвергалась кодированию в utf-8 (кодировку страницы). Однако если такую ссылку вводили в адресную строку браузера руками, то кодировка была windows-1251 (это для русскоязычной windows). Для linux машины, на которой стояла fedora, кодировка была utf-8. как решили проблему? Как обычно: матюгами и напильником.

Источник — http://black-zorro.com/mediawiki/Java_ru_1

As there seems to be some confusion on whether this is possible or not I think I’ll need to provide an extensive example.

The question claims that the (initial) input is a byte[] that contains Windows-1252 encoded data. I’ll call that byte[] ib (for «initial bytes»).

For this example I’ll choose the German word «Bär» (meaning bear) as the input:

byte[] ib = new byte[] { (byte) 0x42, (byte) 0xE4, (byte) 0x72 };
String correctString = new String(ib, "Windows-1252");
assert correctString.charAt(1) == 'u00E4'; //verify that the character was correctly decoded.

(If your JVM doesn’t support that encoding, then you can use ISO-8859-1 instead, because those three letters (and most others) are at the same position in those two encodings).

The question goes on to state that some other code (that is outside of our influence) already converted that byte[] to a String using the UTF-8 encoding (I’ll call that String is for «input String»). That String is the only input that is available to achieve our goal (if ib were available, it would be trivial):

String is = new String(ib, "UTF-8");
System.out.println(is);

This obviously produces the incorrect output «B�».

The goal would be to produce ib (or the correct decoding of that byte[]) with only is available.

Now some people claim that getting the UTF-8 encoded bytes from that is will return an array with the same values as the initial array:

byte[] utf8Again = is.getBytes("UTF-8");

But that returns the UTF-8 encoding of the two characters B and and definitely returns the wrong result when re-interpreted as Windows-1252:

System.out.println(new String(utf8Again, "Windows-1252");

This line produces the output «B�», which is totally wrong (it is also the same output that would be the result if the initial array contained the non-word «Bür» instead).

So in this case you can’t undo the operation, because some information was lost.

There are in fact cases where such mis-encodings can be undone. It’s more likely to work, when all possible (or at least occuring) byte sequences are valid in that encoding. Since UTF-8 has several byte sequences that are simply not valid values, you will have problems.

As there seems to be some confusion on whether this is possible or not I think I’ll need to provide an extensive example.

The question claims that the (initial) input is a byte[] that contains Windows-1252 encoded data. I’ll call that byte[] ib (for «initial bytes»).

For this example I’ll choose the German word «Bär» (meaning bear) as the input:

byte[] ib = new byte[] { (byte) 0x42, (byte) 0xE4, (byte) 0x72 };
String correctString = new String(ib, "Windows-1252");
assert correctString.charAt(1) == 'u00E4'; //verify that the character was correctly decoded.

(If your JVM doesn’t support that encoding, then you can use ISO-8859-1 instead, because those three letters (and most others) are at the same position in those two encodings).

The question goes on to state that some other code (that is outside of our influence) already converted that byte[] to a String using the UTF-8 encoding (I’ll call that String is for «input String»). That String is the only input that is available to achieve our goal (if ib were available, it would be trivial):

String is = new String(ib, "UTF-8");
System.out.println(is);

This obviously produces the incorrect output «B�».

The goal would be to produce ib (or the correct decoding of that byte[]) with only is available.

Now some people claim that getting the UTF-8 encoded bytes from that is will return an array with the same values as the initial array:

byte[] utf8Again = is.getBytes("UTF-8");

But that returns the UTF-8 encoding of the two characters B and and definitely returns the wrong result when re-interpreted as Windows-1252:

System.out.println(new String(utf8Again, "Windows-1252");

This line produces the output «B�», which is totally wrong (it is also the same output that would be the result if the initial array contained the non-word «Bür» instead).

So in this case you can’t undo the operation, because some information was lost.

There are in fact cases where such mis-encodings can be undone. It’s more likely to work, when all possible (or at least occuring) byte sequences are valid in that encoding. Since UTF-8 has several byte sequences that are simply not valid values, you will have problems.

Содержание

  1. Java: Русские буквы и не только.
  2. 1. Введение
  3. java convert String windows-1251 to utf8
  4. 2 Answers 2
  5. Баг или фича в Java: Вывод кириллических символов в консоль
  6. Java utf 8 to windows 1251
  7. Закодируйте строку в UTF-8 на Java
  8. Вступление
  9. Использование метода getBytes()
  10. Кодируйте строку в UTF-8 с помощью стандартных наборов символов Java 7
  11. Git Essentials
  12. Закодируйте строку в UTF-8 с помощью Apache Commons
  13. Вывод

Java: Русские буквы и не только.

1. Введение

Кодировки

Когда я только начинал программировать на языке C, первой моей программой (не считая HelloWorld) была программа перекодировки текстовых файлов из основной кодировки ГОСТ-а (помните такую? 🙂 в альтернативную. Было это в далёком 1991-ом году. С тех пор многое изменилось, но за прошедшие 10 лет подобные программки свою актуальность, к сожалению, не потеряли. Слишком много уже накоплено данных в разнообразных кодировках и слишком много используется программ, которые умеют работать только с одной. Для русского языка существует не менее десятка различных кодировок, что делает проблему ещё более запутанной.

Unicode

В набор символов Unicode входят всевозможные буквы со всякими чёрточками и припендюльками, греческие, математические, иероглифы, символы псевдографики и пр. и пр. В том числе и так любимые нами символы кириллицы (диапазон значений 0x0400-0x04ff). Так что с этой стороны никакой дискриминации нет.

Если Вам интересны конкретные кода символов, для их просмотра удобно использовать программу «Таблица символов» из WinNT. Вот, например, диапазон кириллицы:

2.pic1

Если у Вас другая OS или Вас интересует официальное толкование, то полную раскладку символов (charts) можно найти на официальном сайте Unicode.

Типы char и byte

До появления версии JDK 1.4 набор доступных кодировок определялся только производителем JDK. Начиная с 1.4 появилось новое API (пакет java.nio.charset), при помощи которого Вы уже можете создать свою собственную кодировку (например поддержать редко используемую, но жутко необходимую именно Вам).

Класс String

Источник

java convert String windows-1251 to utf8

I’m trying change keyboard: input cyrylic keyboard, output latin. Example: qwerty +> йцукен

It doesn’t work, can anyone tell me what i’m doing wrong?

2 Answers 2

First java text, String/char/Reader/Writer is internally Unicode, so it can combine all scripts. This is a major difference with for instance C/C++ where there is no such standard.

Now System.in is an InputStream for historical reasons. That needs an indication of encoding used.

The above explicitly sets the conversion for System.in to Cyrillic. Without this optional parameter the default encoding is taken. If that was not changed by the software, it would be the platform encoding. So this might have been correct too.

Now text is correct, containing the Cyrillic from System.in as Unicode.

You would get the UTF-8 bytes as:

The old «recoding» of text was wrong; drop this line. in fact not all Windows-1251 bytes are valid UTF-8 multi-byte sequences.

System.out is a PrintStream, a rather rarely used historic class. It prints using the default platform encoding. More or less rely on it, that the default encoding is correct.

For printing to an UTF-8 encoded file:

Here I have added a Unicode BOM character in front, so Windows Notepad may recognize the encoding as UTF-8. In general one should evade using a BOM. It is a zero-width space (=invisible) and plays havoc with all kind of formats: CSV, XML, file concatenation, cut-copy-paste.

Источник

Баг или фича в Java: Вывод кириллических символов в консоль

Есть много особенностей, про которые желательно знать, программируя на Java, даже если Вы начинающий программист. Под катом я расскажу как вывести кириллические символы в консоль Windows и наглядно это продемонстрирую.
Начнем с простого.

Char — это символьный тип данных. Переменная такого типа занимает 2 байта памяти, так как хранится в кодировке unicode.
С переменными этого типа можно производить только операции присваивания, но зато различными способами. Самый простой из них выглядит так:
c = ‘b’;
Символ можно представить также в виде его кода, записанного в восьмеричной системе счисления:
c = ‘77’;
Где 077 – это обязательно трехзначное число, не большее чем 377 (=255 в десятичной системе счисления).
Или же в шестнадцатеричной системе счисления следующим образом:
c = ‘u12a4’;
Кроме того, существуют специальные символы, такие как знак абзаца, разрыв страницы и др. Знак абзаца запишется, например, так:
c = ‘n’;
Не нужно перечислять их здесь. При необходимости всегда можно заглянуть в справочник.

Теперь внимание. Кодировкой по-умолчанию среды программирования Java является Latin-1. Однако, при выводе в поток System.out символы преобразуются в кодировку по умолчанию для операционной системы. Так для русскоязычной локализации кодировкой по-умолчанию является Windows-1251, для linux таковой будет UTF-8. Однако по задумке Microsoft решили для консоли Windows использовать кодировку Cp866.

Соответственно вывод: для корректного отображения кириллических символов в консоли нужно выводить символы в кодировке Cp866!

Это можно сделать следующим способом:

import java.io.PrintStream;
import java.io.UnsupportedEncodingException;

Источник

Java utf 8 to windows 1251

Доброго времени суток.
При перекодировке строки в Cp1251 в кодировку UTF-8 возникает ошибка в букве И. Она кодируется как <-48, 63), а должна быть <-48, 104>.

String cp1251text = «Иван»;
String utf8text = new String(cp1251text.getBytes(«utf-8»));

В итоге текст utf8text имеет вид �?ван
С другими буквами русского алфавита как с заглавными так и с прописными все нормально.

Не помогают и более изощренные способы — например перекодировка с помощью всяких там файлов, буферов ввода и прочими шаманскими замолодями.
Даже был такой вариант:

На MacOS X все нормально. Там нативный UTF-8 и перекодировки все работают без проблем. На Windows XP хуже. Кто что подскажет?

488319eecbce4f4f39d7e275f72adcb2?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: C0s
Дата: 08.03.10 22:39
Оценка: 1 (1)

Здравствуйте, Madjack, Вы писали:

1) в Java все строки — UCS-2, поэтому неразумно пытаться называть переменные типа String utf8 или cp1251, раз они таковыми не являются
2) понятие кодировки применимо только к последовательности байтов
3) предлагаю свой вариант твоего примера, после которого должно всё стать ясно:

64943d0fcaf4250f57e8b6300633fa5f?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: Blazkowicz
Дата: 09.03.10 06:47
Оценка:

ukliam3

541751b6c7bee0506e3fbf8b6a46e94e?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: Madjack
Дата: 09.03.10 07:07
Оценка:

Здравствуйте, C0s, Вы писали:

C0s>Здравствуйте, Madjack, Вы писали:

C0s>

C0s>1) в Java все строки — UCS-2, поэтому неразумно пытаться называть переменные типа String utf8 или cp1251, раз они таковыми не являются
C0s>2) понятие кодировки применимо только к последовательности байтов
C0s>3) предлагаю свой вариант твоего примера, после которого должно всё стать ясно:

C0s>

Запустил ваш пример. Ошибок не выдало.

541751b6c7bee0506e3fbf8b6a46e94e?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: Madjack
Дата: 09.03.10 07:09
Оценка:

Здравствуйте, Blazkowicz, Вы писали:

B>Здравствуйте, Madjack, Вы писали:

M>>При перекодировке строки в Cp1251 в кодировку UTF-8 возникает ошибка в букве И. Она кодируется как <-48, 63), а должна быть <-48, 104>.
B>Большинство проблем с кодировками именно из-за того что не все понимают что такое кодировка. А кодировка это представление строки в двоичном виде. Строка сама по себе не имеет кодировке. Это просто строка конкретного текста. Кодировки используются для того чтобы строку представить в двоичном виде для последующей передачи или хранения.

Я как раз и говорю о представлении строки в двоичном виде. В байтах. Мне как раз и нужно превратить строку в UTF-8 перед отправкой на другой сервер. Так как с приложением работают люди с MacOS, Windows и других операционных систем, то нужно единое представление данных. В UTF-8. А так как в MacOS все данные уже в UTF-8, а на Windows в Cp1251 то нужно чтото делать, чтобы данные на сервер доходили в единой кодировке. И соответственно приходили с него тоже.

3eeb64a8ab2d62fc5f3fd62a242e88c5?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: GarryIV
Дата: 09.03.10 08:58
Оценка:

Здравствуйте, Madjack, Вы писали:

M>>>При перекодировке строки в Cp1251 в кодировку UTF-8 возникает ошибка в букве И. Она кодируется как <-48, 63), а должна быть <-48, 104>.
B>>Большинство проблем с кодировками именно из-за того что не все понимают что такое кодировка. А кодировка это представление строки в двоичном виде. Строка сама по себе не имеет кодировке. Это просто строка конкретного текста. Кодировки используются для того чтобы строку представить в двоичном виде для последующей передачи или хранения.

M>Я как раз и говорю о представлении строки в двоичном виде. В байтах. Мне как раз и нужно превратить строку в UTF-8 перед отправкой на другой сервер. Так как с приложением работают люди с MacOS, Windows и других операционных систем, то нужно единое представление данных. В UTF-8. А так как в MacOS все данные уже в UTF-8, а на Windows в Cp1251 то нужно чтото делать, чтобы данные на сервер доходили в единой кодировке. И соответственно приходили с него тоже.

Поздно пить боржоми когда у тебя уже строка в объекте java.lang.String

A String represents a string in the UTF-16

Сценарий перекодировок такой

Тут вам не CС++ его строками smile

541751b6c7bee0506e3fbf8b6a46e94e?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: Madjack
Дата: 09.03.10 09:49
Оценка:

Здравствуйте, GarryIV, Вы писали:

GIV>Тут вам не CС++ его строками smile

Объясню всю ситуацию. Есть некое приложение Java. В нем есть один диалог. В диалоге есть поле ввода JTextField и кнопка JButton. Пользователь запускает приложение, вводит свою фамилию и жмет кнопку. После нажатия кнопки:

//Забираем введенный текст сразу в UTF-8
String fio = new String(jtextfield1.getBytes(«utf-8»));
//Отправляем данные юзера на сервак.
soapclient.sendUserFio(base64encode(fio));

Скомпилено и собрано все UTF-8 на макинтоше. На томже макинтоше все работает наура. Проблемы на винде.

Собственно когда строка приходит на сервер то получаем все в UTF-8 за исключением большой заглавной русской буквы И. Она отображается как знак вопроса в ромбике + знак вопроса без ромбика. Никакие шаманства не катят. Подскажите как в String fio получить данные из JTextField в UTF-8 независимо от операционной системы юзера и его кодировки.

64943d0fcaf4250f57e8b6300633fa5f?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: Blazkowicz
Дата: 09.03.10 09:51
Оценка:

ukliam3

64943d0fcaf4250f57e8b6300633fa5f?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: Blazkowicz
Дата: 09.03.10 09:53
Оценка:

Здравствуйте, Madjack, Вы писали:

M>//Забираем введенный текст сразу в UTF-8
M>String fio = new String(jtextfield1.getBytes(«utf-8»));

WTF? confused

ukliam3

541751b6c7bee0506e3fbf8b6a46e94e?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: Madjack
Дата: 09.03.10 09:58
Оценка:
541751b6c7bee0506e3fbf8b6a46e94e?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: Madjack
Дата: 09.03.10 09:59
Оценка:

Здравствуйте, Blazkowicz, Вы писали:

B>Здравствуйте, Madjack, Вы писали:

M>>Я как раз и говорю о представлении строки в двоичном виде. В байтах. Мне как раз и нужно превратить строку в UTF-8 перед отправкой на другой сервер. Так как с приложением работают люди с MacOS, Windows и других операционных систем, то нужно единое представление данных. В UTF-8. А так как в MacOS все данные уже в UTF-8, а на Windows в Cp1251 то нужно чтото делать, чтобы данные на сервер доходили в единой кодировке. И соответственно приходили с него тоже.
B>В твоей ситуации выходит чот строка изначально интерпретировалась неверной кодировкой. В результате чего в объекте находится текст отличный от ожиидаемого. Исправление значения приведенным образом не есть нормальный подход, к тому же, как показывает пример, он не всегда выполним. Нормальный подход должен недопустить появления экземпляра испорченой строки.

Ну так я обратился на форум чтобы мне помогли организовать нормальный подход. Так как у самого не получается.

64943d0fcaf4250f57e8b6300633fa5f?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: Blazkowicz
Дата: 09.03.10 10:02
Оценка:

ukliam3

c128f7c11b1754257ef85cfb88fb8287?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: hrensgory
Дата: 09.03.10 10:05
Оценка:

09.03.2010 12:49, Madjack пишет:

> //Забираем введенный текст сразу в UTF-8
> String fio = new String(jtextfield1.getBytes(«utf-8»));

Проблема здесь. Это бессмысленное с точки зрения java деяние, т.к.
реально вызовется что-то вроде.

String fio = new String(jtextfield1.getBytes(«utf-8»),
платформо_зависимая_кодировка_по_умолчанию);

А вам скорее всего нужно это:

String fio = field.getText(); // или что там нужно
byte[] bytes = fio.getBytes(«utf-8»); // если дальше нужны именно bytes

Поищите статью Сергея Астахова «Java. Русские буквы и не только» — мне
после длительного программирования на C именно она помогла понять что я
делал неправильно.

Здравствуйте, Blazkowicz, Вы писали:

B>Здравствуйте, Madjack, Вы писали:

M>>>>String fio = new String(jtextfield1.getBytes(«utf-8»));
B>>>WTF? confused
M>>А по конкретнее.
B>У JTextField вроде бы нет такого метода. И для чего здесь указание кодировки, если JTextField и так возвращает строку?

Пардон. String text = new String(jtextfield1.getText().getBytes(«utf-8»));
Если использовать String text = jtextfield1.getText(); то возвращается строка в текущей кодировке системы. А мне нужен уже Utf-8.

541751b6c7bee0506e3fbf8b6a46e94e?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: Madjack
Дата: 09.03.10 10:18
Оценка:

Здравствуйте, hrensgory, Вы писали:

H>09.03.2010 12:49, Madjack пишет:

>> //Забираем введенный текст сразу в UTF-8
>> String fio = new String(jtextfield1.getBytes(«utf-8»));

H>Проблема здесь. Это бессмысленное с точки зрения java деяние, т.к.
H>реально вызовется что-то вроде.

H>String fio = new String(jtextfield1.getBytes(«utf-8»),
H>платформо_зависимая_кодировка_по_умолчанию);

H>А вам скорее всего нужно это:

H>String fio = field.getText(); // или что там нужно
H>byte[] bytes = fio.getBytes(«utf-8»); // если дальше нужны именно bytes

H>Поищите статью Сергея Астахова «Java. Русские буквы и не только» — мне
H>после длительного программирования на C именно она помогла понять что я
H>делал неправильно.

H>—
H>WBR,
H>Serge.

Мне нужны не байты. До байтов я докапался когда искал проблему, в каком месте коржется буква И. Механизм прост. Пользователь вводит строку, мне нужно эту строку получить в utf-8 и отправить на удаленный сервер.

H>String fio = field.getText(); // или что там нужно

Здесь строка fio будет в кодировке системы. Если на винде то в cp1251. А мне нужна в utf-8. Соответственно вместо:
String text = jtextfield1.getText();
bytes[] bytes = text.getBytes(«utf-8»);
String utftext = new String(bytes);

я пишу одной строчкой
String text = jtextfield1.getText().getBytes(«utf-8»);

1befe4dc86a9c34f06f346c06c1d287a?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: Golden Wolf http://not-null.com
Дата: 09.03.10 10:18
Оценка:

Здравствуйте, Madjack, Вы писали:

M>Собственно когда строка приходит на сервер то получаем все в UTF-8 за исключением большой заглавной русской буквы И. Она отображается как знак вопроса в ромбике + знак вопроса без ромбика. Никакие шаманства не катят. Подскажите как в String fio получить данные из JTextField в UTF-8 независимо от операционной системы юзера и его кодировки.

А вы уверенны, что проблема ДО передачи данных на сервер?
Каким образом данные поступают на сервер?

64943d0fcaf4250f57e8b6300633fa5f?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: Blazkowicz
Дата: 09.03.10 10:20
Оценка:

ukliam3

541751b6c7bee0506e3fbf8b6a46e94e?s=32&r=g&d=http%3a%2f%2frsdn.org%3a80%2fImages%2fDefaultGravatar От: Madjack
Дата: 09.03.10 10:24
Оценка:

Да я не становлюсь в позу=) Я просто проверил все на практике. Если не делать тех манипуляций с перекодировками то до сервера приходит строка в cp1251.

Источник

Закодируйте строку в UTF-8 на Java

В этом уроке мы рассмотрим, как кодировать строку в UTF-8 на Java – с помощью стандартных наборов символов, getBytes() с помощью ByteBuffer и Apache Commons с примерами.

Вступление

При работе со строками | в Java нам часто приходится кодировать их в определенную кодировку, такую как UTF-8 .

Кодовая точка может представлять отдельные символы, но также иметь другие значения, например, для форматирования. “Переменная ширина” означает, что он кодирует каждую кодовую точку с разным количеством байтов (от одного до четырех), и в качестве меры экономии места обычно используемые кодовые точки представлены меньшим количеством байтов, чем те, которые используются реже.

Примечание: Java кодирует все строки в UTF-16, который использует минимум два байта для хранения кодовых точек. Зачем тогда нам нужно было бы конвертировать в UTF-8?

Не все входные данные могут быть UTF-16, или UTF-8, если на то пошло. На самом деле вы можете получить строку в кодировке ASCII, которая поддерживает не так много символов, как UTF-8. Кроме того, не все выходные данные могут обрабатывать UTF-16, поэтому имеет смысл преобразовать в более универсальный UTF-8.

Давайте выпишем пару строк:

Как только мы создадим эти строки и закодируем их в виде символов ASCII, мы сможем их распечатать:

Чтобы избежать этой проблемы, мы можем предположить, что не все входные данные уже могут быть закодированы по нашему вкусу – и закодировать их, чтобы самим решить такие случаи. Существует несколько способов кодирования строки в UTF-8 на Java.

Использование метода getBytes()

Теперь выводится та же самая строка, с которой мы начали, но закодированная в UTF-8:

Кодируйте строку в UTF-8 с помощью стандартных наборов символов Java 7

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

Выполнение этого кода приводит к:

Закодируйте строку в UTF-8 с помощью Apache Commons

Чтобы мы могли использовать кодек Apache Commons, нам нужно добавить его в наш проект в качестве внешней зависимости.

Используя Maven, давайте добавим зависимость commons-кодек в ваш pom.xml файл:

В качестве альтернативы, если вы используете Gradle:

Это позволяет нам преобразовывать строки в байты и из байтов, используя различные кодировки, требуемые спецификацией Java. Этот класс является нулевым и потокобезопасным, поэтому у нас есть дополнительный уровень защиты при работе со строками.

Если вы используете Gradle:

И теперь мы можем использовать почти тот же подход, что и с обычными строками:

Благодаря этому подход является потокобезопасным и нулевым:

Вывод

Источник

Понравилась статья? Поделить с друзьями:
  • Java для windows 7 x32 скачать торрент
  • Java для windows 10 x64 скачать бесплатно последняя версия
  • Java для libreoffice base установить windows
  • Java windows в автономном режиме 64 разрядная версия
  • Java windows path to linux path