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

Если кодировка указана не была, то по умолчанию предполагается кодировка UTF-8. На XML-парсер возложена обязанность корректно прочитать заголовок и использовать

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

Курсовой проект

Компьютеры, программирование

Другие курсовые по предмету

Компьютеры, программирование

Сдать работу со 100% гаранией
nputStream 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");

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

8-ми битовые кодировки русских букв

Вот основные 8-ми битовые кодировки русских букв, получившие распространение:

КодировкаАреал распространенияОсновное название в JavaIBM-866MS-DOS, Windows (OEM-кодировка), OS/2Cp866Windows-1251Windows (Ansi-кодировка)Cp1251КОИ-8Unix, большинство русскоязычных писем в InternetKOI8_RISO-8859-5UnixISO8859_5Macintosh CyrillicMacMacCyrillicПомимо основного названия можно использовать синонимы. Набор их может отличаться в разных версиях JDK. Вот список от JDK 1.3.1:

Cp1251:

Windows-1251

Cp866:

IBM866

IBM-866

866

CP866

CSIBM866

KOI8_R:

KOI8-R

KOI8

CSKOI8R

ISO8859_5:

ISO8859-5

ISO-8859-5

ISO_8859-5

ISO_8859-5:1988

ISO-IR-144

8859_5

Cyrillic

CSISOLatinCyrillic

IBM915

IBM-915

Cp915

915

Причём синонимы, в отличии от основного имени нечувствительны к регистру символов - такова особенность реализации.

Стоит отметить, что эти кодировки на некоторых JVM могут отсутствовать. Например, с сайта Sun можно скачать две разные версии JRE - US и International. В US версии присутствует только минимум - ISO-8859-1, ASCII, Cp1252, UTF8, UTF16 и несколько вариаций двухбайтового Unicode. Всё прочее есть только в International варианте. Иногда из-за этого можно нарваться на грабли с запуском программы, даже если ей не нужны русские буквы. Типичная ошибка, возникающая при этом:

Error occurred during initialization of VM

java/lang/ClassNotFoundException: sun/io/ByteToCharCp1251

Возникает она, как не трудно догадаться, из-за того, что JVM, исходя из русских региональных настроек пытается установить кодировку по умолчанию в Cp1251, но, т.к. класс поддержки таковой отсутствует в US версии, закономерно обламывается.

Файлы и потоки данных

Так же как и байты концептуально отделены от символов, в Java различаются потоки байтов и потоки символов. Работу с байтами представляют классы, которые прямо или косвенно наследуют классы InputStream или OutputStream (плюс класс-уникум RandomAccessFile). Работу с символами представляет сладкая парочка классов Reader/Writer (и их наследники, разумеется).

Для чтения/записи не преобразованных байтов используются потоки байтов. Если известно, что байты представляют собой только символы в некоторой кодировке, можно использовать специальные классы-преобразователи InputStreamReader и OutputStreamWriter, чтобы получить поток символов и работать непосредственно с ним. Обычно это удобно в случае обычных текстовых файлов или при работе с многими сетевыми протоколами Internet. Кодировка символов при этом указывается в конструкторе класса-преобразователя. Пример:

// Строка Unicode

String string = "...";

// Записываем строку в текстовый файл в кодировке Cp866

PrintWriter pw = new PrintWriter // класс с методами записи строк

(new OutputStreamWriter // класс-преобразователь

(new FileOutputStream // класс записи байтов в файл

("file.txt"), "Cp866");

pw.println(string); // записываем строку в файл

pw.close();

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

// Строка Unicode

String string = "...";

// Записываем строку в текстовый файл в двух кодировках (Cp866 и Cp1251)

OutputStream os = new FileOutputStream("file.txt"); // класс записи байтов в файл

// Записываем строку в кодировке Cp866

os.write( string.getBytes("Cp866") );

// Записываем строку в кодировке Cp1251

os.write( string.getBytes("Cp1251") );

os.close();

Консоль в Java традиционно представлена потоками, но, к сожалению, не символов, а байтов. Дело в том, что потоки символов появились только в JDK 1.1 (вместе со всем механизмом кодировок), а доступ к консольному вводу/выводу проектировался ещё в JDK 1.0, что и привело к появлению уродца в виде класса PrintStream. Этот класс используется в переменных System.out и System.err, которые собственно и дают доступ к выводу на консоль. По всем признакам это поток байтов, но с кучей методов записи строк. Когда Вы записываете в него строку, внутри происходит конвертация в байты с использованием кодировки по умолчанию, что в случае виндов, как правило, неприемлемо - кодировка по умолчанию будет Cp1251 (Ansi), а для консольного окна обычно нужно использовать Cp866 (OEM). Эта ошибка была зарегистрированна ещё в 97-ом году (4038677) но Sun-овцы исправлять её вроде не торопятся. Так как метода установки кодировки в PrintStream нет, для решения этой проблемы можно подменить стандартный класс на собственный при помощи методов System.setOut() и System.setErr(). Вот, например, обычное начало в моих программах:

...

public static void main(String[] args)

{

// Установка вывода консольных сообщений в нужной кодировке

try

{

String consoleEnc = System.getProperty("console.encoding","Cp866");

System.setOut(new CodepagePrintStream(System.out,consoleEnc) );

System.setErr(new CodepagePrintStream(System.err,consoleEnc) );

}

catch(UnsupportedEncodingException e)

{

System.out.println("Unable to setup console codepage: " + e);

}

...

Исходники класса CodepagePrintStream Вы можете найти на данном сайте: CodepagePrintStream.java.

Если Вы сами конструируете формат данных, я рекомендую Вам использовать одну из многобайтовых кодировок. Удобнее всего обычно формат UTF8 - первые 128 значений (ASCII) в нём кодируются одним байтом, что часто может значительно уменьшить общий объём данных (не зря эта кодировка принята за основу в мире XML). Но у UTF8 есть один недостаток - кол-во требуемых байтов зависит от кода символов. Там, где это критично можно использовать один из двухбайтовых форматов Unicode (UnicodeBig или UnicodeLittle).

Базы данных

Для того, чтобы прочитать корректно символы из БД, обычно достаточно указать JDBC-драйверу на нужную кодировку символов в БД. Как именно - это зависит от конкретного драйвера. Сейчас уже многие драйвера поддерживают подобную настройку, в отличии от недавнего прошлого. Далее приведены несколько известных мне примеров.

Мост JDBC-ODBC

Это один из самых часто используемых драйверов. Мост из JDK 1.2 и старше можно легко настроить на нужную кодировку. Это делается добавлением дополнительного свойства charSet в набор параметров, передаваемых для открытия соединения с базой. По умолчанию используется file.encoding. Делается это примерно так:

// Параметры соединения с базой

Properties connInfo = new Properties();

connInfo.put("user", username);

connInfo.put("password", password);

connInfo.put("charSet", "Cp1251");

// Устанавливаем соединение

Connection db = DriverManager.getConnection(dataurl, connInfo);

Драйвер JDBC-OCI от Oracle 8.0.5 под Linux

При получении данных из БД, данный драйвер определяет "свою" кодировку при помощи переменной окружения NLS_LANG. Если эта переменная не найдена, то он считает что кодировка - ISO-8859-1. Весь фокус в том, что NLS_LANG должна быть именно переменной окружения (устанавливаемой командой set), а не системное свойство Java (типа file.encoding). В случае использования драйвера внутри servlet engine Apache+Jserv, переменную окружения можно задать в файле jserv.properties:

wrapper.env=NLS_LANG=American_America.CL8KOI8R

Информацию об этом прислал Сергей Безруков, за что ему отдельное спасибо.

Драйвер JDBC-thin от Oracle

Сей драйвер вроде как не

Похожие работы

< 1 2 3 4 5 6 > >>