Курсовая: Java: Русские буквы и не только…Notice: Undefined variable: ref_img in /home/area7ru/area7.ru/docs/referat.php on line 323
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 Сей драйвер вроде как не требует особой настройки. По крайней мере в документации об этом ни слова. По всей видимости у Oracle в протоколе обмена ходит нормальный Unicode, правда за исключением составных типов (типы Object и Collection). Если Вы пользуетесь сложными типами, то не забудьте про отдельный zip с поддержкой кодировок (с именем типа nls_charset12.zip), который скачивается отдельно. В противном случае драйвер будет поддерживать только минимум (US7ASCII, WE8DEC, WE8ISO8859P1 и UTF8) и, если БД была создана в другой кодировке, то при получении строковых значений из составных типов будет сплошной 16-ричный мусор (если включён log у DriverManager, то при этом будет видна ругань на неизвестную кодировку). Самая большая проблема, с которой многие сталкиваются - некорректная кодировка сообщений об ошибках. Дело в том, что оригинальные драйвера от Oracle 8.1.7 и 9.0.1 содержат некорректно сформированные файлы ресурсов с текстами русских сообщений. Драйвера от 9.0.2 и 9.2.x уже нормальные. Эти файлы ресурсов можно довольно легко пропатчить при помощи утилиты native2ascii. Готовый скрипт патча можно найти здесь: http://java.ksu.ru/sources/oracle/ (или здесь - oracle_jdbc_repair.bat). Можно также поменять текущий язык сессии выполнив команду "ALTER SESSION SET NLS_LANGUAGE=English". При этом сообщения станут выдаваться на английском. Ну а самый правильный путь - использовать последние версии драйверов от 9.2.x, благо что они совместимы со старыми версиями Oracle и к тому же содержат исправления других ошибок. Драйвер JDBC для работы с DBF (com.hxtt.sql.dbf.DBFDriver, бывший zyh.sql.dbf.DBFDriver) Данный драйвер только недавно научился работать с русскими буквами. Настройка выполняется немного по разному в зависимости от версии драйвера Версии Beta 5.4 (от 02.04.2002) и более поздние уже нормально воспринимают настройку charSet. В версиях Beta 5.2 (от 30.07.2001) и Beta 5.3 (30.11.2001), хоть он и сообщает по getPropertyInfo() что он понимает свойство charSet - это фикция. Реально же настроить кодировку можно установкой свойства CODEPAGEID. Для русских символов там доступны два значения - "66" для Cp866 и "C9" для Cp1251. Пример: // Параметры соединения с базой Properties connInfo = new Properties(); // Кодировка Cp866 connInfo.put("charSet", "Cp866"); connInfo.put("CODEPAGEID", "66"); // Устанавливаем соединение Connection db = DriverManager.getConnection("jdbc:DBF:/C:/MyDBFFiles", connInfo); Если у Вас DBF-файлы формата FoxPro, то у них своя специфика. Дело в том, что FoxPro сохраняет в заголовке файла ID кодовой страницы (байт со смещением 0x1D), которая использовалась при создании DBF-ки. При открытии таблицы драйвер использует значение из заголовка, а не параметр "CODEPAGEID" (параметр в этом случае используется только при создании новых таблиц). Соответственно, чтобы всё работало нормально, DBF-файл должен быть создан с правильной кодировкой - иначе будут проблемы. MySQL (org.gjt.mm.mysql.Driver) С этим драйвером тоже всё довольно просто: // Параметры соединения с базой Properties connInfo = new Properties(); connInfo.put("user",user); connInfo.put("password",pass); connInfo.put("useUnicode","true"); connInfo.put("characterEncoding","KOI8_R"); Connection conn = DriverManager.getConnection(dbURL, props); InterBase (interbase.interclient.Driver) Для этого драйвера работает параметр "charSet": // Параметры соединения с базой Properties connInfo = new Properties(); connInfo.put("user", username); connInfo.put("password", password); connInfo.put("charSet", "Cp1251"); // Устанавливаем соединение Connection db = DriverManager.getConnection(dataurl, connInfo); Однако не забудьте при создании БД и таблиц указать кодировку символов. Для русского языка можно использовать значения "UNICODE_FSS" или "WIN1251". Пример: CREATE DATABASE *E:ProjectHoldingDataBaseHOLDING.GDB* PAGE_SIZE 4096 DEFAULT CHARACTER SET UNICODE_FSS; CREATE TABLE RUSSIAN_WORD ( "NAME1" VARCHAR(40) CHARACTER SET UNICODE_FSS NOT NULL, "NAME2" VARCHAR(40) CHARACTER SET WIN1251 NOT NULL, PRIMARY KEY ("NAME1") ); В версии 2.01 InterClient присутствует ошибка - классы ресурсов с сообщениями для русского языка там неправильно скомпилированы. Скорей всего разработчики просто забыли указать кодировку исходников при компиляции. Есть два пути исправления этой ошибки: Использовать interclient-core.jar вместо interclient.jar. При этом русских ресурсов просто не будет, и автоматом подхватятся английские. Перекомпилировать файлы в нормальный Unicode. Разбор class-файлов - дело неблагодарное, поэтому лучше воспользоваться JAD-ом. К сожалению JAD, если встречает символы из набора ISO-8859-1, выводит их в 8-иричной кодировке, так что воспользоваться стандартным перекодировщиком native2ascii не удастся - придётся написать свой (программа Decode). Если Вам не хочется заморачиваться с этими проблемами - можете просто взять готовый файл с ресурсами (пропатченый jar с драйвером - interclient.jar, отдельные классы ресурсов - interclient-rus.jar). JayBird (org.firebirdsql.jdbc.FBDriver) Для этого драйвера указание кодировки отличается от InterClient: // Параметры соединения с базой Properties connInfo = new Properties(); connInfo.put("user", username); connInfo.put("password", password); connInfo.put("lc_ctype","WIN1251"); // Устанавливаем соединение Connection db = DriverManager.getConnection(dataurl, connInfo); SAP DB (com.sap.dbtech.jdbc.DriverSapDB) Для этого драйвера можно включить использование Unicode задав параметр unicode в true. В противном случае будет использованна жёстко зашитая ISO-8859-1, с вытекающими отсюда осточертевшими "??????". Кроме того существует доработанная напильником версия (sapdbc.zip), которая позволяет задавать параметр charSet. DataSource (javax.sql.DataSource) Если получение соединение производится через использование DataSource, то поддержку настройки используемой кодировки должна содержать конкретная реализация, которая регистрируется в JNDI. Для примера можно взять реализацию от InterClient (interbase.interclient.DataSource). В этом классе есть метод setCharSet(), используя который можно указать необходимую кодировку. Пример: interbase.interclient.DataSource dataSource = new interbase.interclient.DataSource(); dataSource.setServerName("pongo"); dataSource.setDatabaseName("/databases/employee.gdb"); dataSource.setCharSet("Cp1251"); javax.naming.Context context = new javax.naming.InitialContext(); context.bind("jdbc/EmployeeDB", dataSource); Другой пример - реализация от DBF-драйвера (com.hxtt.sql.HxttDataSource): Properties prop=new Properties(); prop.setProperty("urlPrefix","jdbc:DBF:"); prop.setProperty("user","aaaaa"); prop.setProperty("password","vvvvccc"); prop.setProperty("database","dbffiles");//Change it to yourdbfdir prop.setProperty("charSet","Cp866"); DataSource dataSource=new com.hxtt.sql.HxttDataSource(prop); context.bind(name, dataSource); Но даже настроив JDBC-драйвер на нужную кодировку в некоторых случаях можно нарваться на неприятности. Например, при попытке использования новых замечательных скролируемых курсоров стандарта JDBC 2 в мосте JDBC-ODBC из JDK 1.3.x довольно быстро можно обнаружить, что русские буквы там просто не работают (метод updateString()). С этой ошибкой связанна небольшая история. Когда я впервые её обнаружил (под JDK 1.3 rc2), я зарегистрил её на BugParade (4335564). Когда вышла первая бета JDK 1.3.1, эту ошибку пометили как пофиксеную. Обрадованный, я скачал эту бету, запустил тест - не работает. Написал Sun-овцам об этом - в ответ мне написали, что фикс будет включён в последующие релизы. Ну ладно, подумал я, подождём. Время шло, вышел релиз 1.3.1, а потом и бета 1.4. Наконец я нашёл время проверить - опять не работает. Мать, мать, мать... - привычно откликнулось эхо. После гневного письма в Sun они завели новую ошибку (4486195), которую отдали на растерзание индийскому филиалу. Индусы пошаманили над кодом, и заявили, что в 1.4 beta3 всё исправлено. Скачал я эту версию, запустил под ним тестовый пример, вот результат - 4546083. Как оказалось, та beta3, которую раздают на сайте (build 84) это не та beta3, куда был включён окончательный фикс (build 87). Теперь обещают, что исправление войдёт в 1.4 rc1... Ну, в общем, Вы понимаете :-) Русские буквы в исходниках Java-программ Как уже упоминалось, при выполнении программы используется Unicode. Исходные же файлы пишутся в обычных редакторах. Я пользуюсь Far-ом, у Вас, наверняка есть свой любимый редактор. Эти редакторы сохраняют файлы в 8-битовом формате, а значит, что к этим файлам также применимы рассуждения, аналогичные приведённым выше. Разные версии компиляторов немного по разному выполняют преобразование символов. В ранних версиях JDK 1.1.x используется настройка file.encoding, которую можно поменять при помощи нестандартной опции -J. В более новых (как сообщил Денис Кокарев - начиная с 1.1.4) был введён дополнительный параметр -encoding, при помощи которого можно указать используемую кодировку. В скомпилированных классах строки представлены в виде Unicode (точнее в модифицированном варианте формата UTF8), так что самое интересное происходит при компиляции. Поэтому, самое главное - выяснить, в какой кодировке у Вас исходники и указать правильное значение при компиляции. По умолчанию будет использован всё тот же пресловутый file.encoding. Пример вызова компилятора: javac -encoding=KOI8_R ... Кроме использования этой настройки есть ещё один метод - указывать буквы в формате "uXXXX", где указывается код символа. Этот метод работает со всеми версиями, а для получения этих кодов можно использовать стандартную утилиту native2ascii. Если Вы пользуетесь каким-либо IDE, то у него могут быть свои глюки. Зачастую эти IDE пользуются для чтения/сохранения исходников кодировку по умолчанию - так что обращайте внимание на региональные настройки своей ОС. Кроме этого могут быть и явные ошибки - например довольно неплохая IDE-шка CodeGuide плохо переваривает заглавную русскую букву "Т". Встроенный анализатор кода принимает эту букву за двойную кавычку, что приводит к тому, что корректный код воспринимается как ошибочный. Бороться с этим можно (заменой буквы "Т" на её код "u0422"), но неприятно. Судя по всему где-то внутри парсера применяется явное преобразование символов в байты (типа: byte b = (byte)c), поэтому вместо кода 0x0422 (код буквы "Т") получается код 0x22 (код двойной кавычки). Другая проблема встречается у JBuilder, но она больше связанна с эргономикой. Дело в том, что в JDK 1.3.0, под которым по умолчанию работает JBuilder, имеется бага (4312168), из-за которой вновь создаваемые окна GUI при активизации автоматически включают раскладку клавиатуры в зависимости от региональных настроек ОС. Т.е. если у Вас русские региональные настройки, то он будет постоянно пытаться переключиться на русскую раскладку, что при написании программ жутко мешается. На сайте JBuilder.ru есть парочка патчиков которые меняют текущую локаль в JVM на Locale.US, но самый лучший способ - перейти на JDK 1.3.1, в котором данная бага пофиксена. Начинающие пользователи JBuilder могут также встретиться с такой проблемой - русские буквы сохраняются в виде кодов "uXXXX". Чтобы этого избежать, надо в диалоге Default Project Properties, закладка General, в поле Encoding поменять Default на Cp1251. Если Вы используете для компиляции не стандартный javac, а другой компилятор - обратите внимание на то, как он выполняет преобразование символов. Например, некоторые версии IBM-овского компилятора jikes не понимают, что бывают кодировки, отличные от ISO-8859-1 :-). Существуют версии, пропатченые на этот счёт, но часто там тоже зашивается некоторая кодировка - нет такого удобства, как в javac. JavaDoc Для генерации HTML-документации по исходникам используется утилита javadoc, входящая в стандартную поставку JDK. Для указания кодировок у неё есть аж 3 параметра: -encoding - эта настройка задаёт кодировку исходников. По умолчанию используется file.encoding. -docencoding - эта настройка задаёт кодировку генерируемых HTML-файлов. По умолчанию используется file.encoding. -charset - эта настройка задаёт кодировку, которая будет записываться в заголовки генерируемых HTML-файлов (<meta http-equiv=Content-Type" content=text/html; charset=...>). Очевидно, что она должна совпадать с предыдущей настройкой. Если данная настройка опущена, то тег meta добавляться не будет. Русские буквы в файлах properties Для чтения файлов properties используются методы загрузки ресурсов, которые работают специфичным образом. Собственно для чтения используется метод Properties.load, который не использует file.encoding (там в исходниках жёстко указана кодировка ISO-8859-1), поэтому единственный способ указать русские буквы - использовать формат "uXXXX" и утилиту native2ascii. Метод Properties.save работает по разному в версиях JDK 1.1 и 1.2. В версиях 1.1 он просто отбрасывал старший байт, поэтому правильно работал только с англицкими буквами. В 1.2 делается обратное преобразование в "uXXXX", так что он работает зеркально к методу load. Если файлы properties у Вас загружаются не как ресурсы, а как обычные файлы конфигурации, и Вас не устраивает такое поведение - выход один, написать собственный загрузчик. Русские буквы в Servlet-ах. Ну, для чего эти самые Servlet-ы нужны, я думаю, Вы в курсе. Если нет - то лучше сначала прочитать документацию. Здесь же рассказывается только об особенностях работы с русскими буквами. Так в чём же особенности? Когда Servlet посылает ответ клиенту, есть два способа послать этот ответ - через OutputStream (метод getOutputStream()) или через PrintWriter (метод getWriter()). В первом случае Вы записываете массивы байтов, поэтому применимы вышеописанные методы записи в потоки. В случае же PrintWriter, он использует установленную кодировку. В любом случае необходимо правильно указать используемую кодировку при вызове метода setContentType(), для того, чтобы было правильное преобразование символов на стороне сервера. Это указание должно быть сделано перед вызовом getWriter() или перед первой записью в OutputStream. Пример: public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { // Установка кодировки ответа // Учтите, что некоторые engine не допускают // наличие пробела между *;* и *charset* response.setContentType("text/html;charset=Windows-1251"); PrintWriter out = response.getWriter(); // Отладочный вывод названия кодировки для проверки out.println( "Encoding: " + response.getCharacterEncoding() ); ... out.close(); } Это по поводу отдачи ответов клиенту. Со входными параметрами, к сожалению не так просто. Входные параметры кодируются броузером побайтно в соответствии с MIME-типом "application/x-www-form-urlencoded". Как рассказал Алексей Менделев русские буквы броузеры кодируют, используя текущую установленную кодировку. Ну и, разумеется, ничего о ней не сообщают. Соответственно, например, в JSDK версий от 2.0 до 2.2 это никак не проверяется, а то, что за кодировка будет использована для преобразования - зависит от используемого engine. Начиная со спецификации 2.3 появилась возможность устанавливать кодировку для javax.servlet.ServletRequest - метод setCharacterEncoding(). Эту спецификацию уже поддерживают последние версии Resin и Tomcat. Таким образом, если Вам повезло, и у Вас стоит сервер с поддержкой Servlet 2.3, то всё довольно просто: public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { // Кодировка сообщений request.setCharacterEncoding("Cp1251"); String value = request.getParameter("value"); ... В применении метода request.setCharacterEncoding() есть одна существенная тонкость - он должен быть применен до первого обращения к запросу за данными (например request.getParameter()). Если Вы используете фильтры, которые обрабатывают запрос до того как он приходит в сервлет, есть ненулевая вероятность того, что в одном из фильтров может произойти чтение какого-нибудь параметра из запроса (например для авторизации) и request.setCharacterEncoding() в сервлете не сработает. Потому идеологически более правильно написать фильтр, устанавливающий кодировку запроса. При этом он должен стоять первым в цепочке фильтров в web.xml. Пример такого фильтра: import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class CharsetFilter implements Filter { // кодировка private String encoding; public void init(FilterConfig config) throws ServletException { // читаем из конфигурации encoding = config.getInitParameter("requestEncoding"); // если не установлена - устанавливаем Cp1251 if( encoding==null ) encoding=Cp1251"; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain next) throws IOException, ServletException { request.setCharacterEncoding(encoding); next.doFilter(request, response); } public void destroy(){} } И его конфигурации в web.xml: <!--CharsetFilter start--> <filter> <filter-name>Charset Filter</filter-name> <filter-class>CharsetFilter</filter-class> <init-param> <param-name>requestEncoding</param-name> <param-value>Cp1251</param-value> </init-param> </filter> <filter-mapping> <filter-name>Charset Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--CharsetFilter end--> Если же Вам не повезло, и у Вас более старая версия - для достижения результата придётся поизвращаться: Оригинальный способ работы с кодировками предлагает Russian Apache - здесь расписано, как именно. Своё решение проблемы так же предложил Вячеслав Педак. Ну а самый простейший вариант извлечь таки символы - передавать в комплекте параметров имя кодировки (или, если вы уверены в текущей кодировке броузера, использовать предопределённую кодировку) и использовать метод перекодировки символов: public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { // Кодировка сообщений, использованная engine // Некоторые используют ISO-8859-1, некоторые кодировку // по умолчанию - единообразия тут нет String requestEnc = "ISO-8859-1"; // Кодировка, установленная в броузере String clientEnc = request.getParameter("charset"); if( clientEnc==null ) clientEnc=Cp1251"; // Получение параметра String value = request.getParameter("value"); // if( value!=null ) value = new String(value.getBytes(requestEnc),clientEnc); ... JSP Технология JSP (Java Server Pages) очень похожа на сервлеты. По сути дела сервер, при запросе в первый раз на лету генерит из jsp-страниц код сервлета, компилирует его и запускает его как обычный сервлет. Поэтому у JSP возникают схожие проблемы при работе с русскими буквами. Однако решаются они немного по другому. Есть три места где могут возникнуть трудности - русские буквы внутри самой jsp-страницы, в ответе клиенту и в запросе от клиента. Первые два решаются заданием в начале страницы тега page: <%@page contentType=text/html;charset=Windows-1251" %> Увидев эту директиву сервер понимает, что страница записана в указанной кодировке, и что в сгенерённый код надо добавить вызов response.setContentType() с указанным contentType. Если сервер поддерживает спецификацию Servlet 2.3, то он также добавит и вызов request.setCharacterEncoding() с нужной кодировкой, таким образом автоматом решая и третью проблему. Для более старых серверов для раскодирования параметров в запросе клиента надо применять ухищрения, аналогичные описанным в разделе по сервлетам. Для примера, для того, чтобы настроить JSP-форум Jive для работы с русскими буквами надо откорректировать следующие файлы: /jive/header.jsp /jive/admin/header.jsp В них надо в начало добавить строчку <%@page contentType=text/html;charset=UTF-8" %> Вместо UTF-8 можно использовать любую кодировку, поддерживающую русские буквы - всё зависит от вкусов и предпочтений. Информацию об этом прислал Алексей Епишкин, за что ему отдельное спасибо. В некоторых серверах встречаются баги, связанные с русскими буквами в JSP. Например, сервер Orion не любит русскую букву "Т" - он вместо неё в сгенерённый сервлет подставляет символ кавычки. Там во внутренностях есть примерно такой код: ... switch( charstring.c1(i) ) ... public final char c1(int i) { if(i < 0 || i >= length) throw new StringIndexOutOfBoundsException(i); else return (char)(data[offset + i] & 0xff); data - это массив типа char[]. Как видно, ошибка тут тривиальна - разработчик почему-то был уверен что символы с кодами больше 255 - это ошибка природы. :-) JavaMail Пакет JavaMail предназначен для работы с электронными письмами. При помощи этого пакета Вы можете отправлять и принимать письма через различные протоколы. Разные протоколы по разному обрабатывают национальные символы. Самые распространённые на данный момент протоколы Internet основаны на старом стандарте RFC-822. Согласно этому стандарту в служебных полях (заголовках) писем разрешено посылать только символы кодировки ASCII, т.е. только латинские буквы (первые 128 символов Unicode). Очевидно, что это неудобно, т.к. часто очень хочется писать, например в поле Subject (тема письма) или в полях From/To (имя и адрес отправителя/получателя) русский текст. Для того, чтобы решить эту проблему был придуман стандарт кодирования MIME (RFC 2047). Он позволяет в некоторых полях заголовка (не во всех) использовать национальные символы при помощи специального кодирования (Base64 или QuotedPrintable). Для представления писем в JavaMail используется класс javax.mail.Message. Это абстрактный класс, реальное же поведение определяется наследниками. Методы, определённые в нём работают только с обычными Java-строками (String). Для протоколов Internet обычно используется наследник javax.mail.internet.MimeMessage, который помимо базовых методов добавляет методы, в которых можно дополнительно указывать кодировку, которую следует использовать для писем. Для кодирования используется вспомогательный класс javax.mail.internet.MimeUtility. Класс MimeMessage обычно сам обращается к нему для кодирования/раскодирования заголовков, но, если Вы напрямую обращаетесь к заголовкам (методы getHeader()/setHeader()/addHeader()), то для их кодирования/раскодирования Вам придётся обращаться к методам MimeUtility самому. Если Вы не указываете кодировку письма, то будет использована кодировка по умолчанию - обычно используется file.encoding, но её можно перекрыть специальной системной настройкой "mail.mime.charset". Это разумно, т.к. часто кодировка по умолчанию в системе отличается от стандартной кодировки Internet. Для русскоязычных писем в Internet стандартом де-факто стала кодировка КОИ-8. Вы, конечно, можете указать и другую, но шанс, что принимающая сторона не сможет прочитать такое письмо очень велик. Надо учитывать также, что в JavaMail различаются два стандарта наименования кодировок - стандарт MIME и стандарт Java. Для большинства кодировок имена MIME уже поддерживаются в Java при помощи механизма синонимов. Например, для кодировки "Cp1251" (название Java) существует синоним "Windows-1251" (название MIME). Для тех кодировок, для которых такие синонимы отсутствуют, они поддерживаются внутри JavaMail. Для этого загружается файл javamail.charset.map из подкаталога "/META-INF" из того jar-файла, откуда был загружен пакет JavaMail. Для указания кодировки при вызове методов JavaMail следует использовать только MIME-имена, в противном случае получатель не сможет распознать использованную кодировку (если только на другом конце не тоже Java :-). Вот простой пример отправки письма при помощи JavaMail: import java.util.Properties; import javax.mail.Session; import javax.mail.Message; import javax.mail.Transport; import javax.mail.internet.MimeMessage; import javax.mail.internet.InternetAddress; public class MailTest { static final String ENCODING = "koi8-r"; static final String FROM = "myaccount@mydomail.ru"; static final String TO = "myaccount@mydomail.ru"; public static void main(String args[]) throws Exception { Properties mailProps = new Properties(); mailProps.put("mail.store.protocol","pop3"); mailProps.put("mail.transport.protocol","smtp"); mailProps.put("mail.user","myaccount"); mailProps.put("mail.pop3.host","mail.mydomail.ru"); mailProps.put("mail.smtp.host","mail.mydomail.ru"); Session session = Session.getDefaultInstance(mailProps); MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress(FROM)); message.setRecipient(Message.RecipientType.TO, new InternetAddress(TO)); message.setSubject("Тестовое письмо",ENCODING); message.setText("Текст тестового письма",ENCODING); Transport.send(message); } } XML/XSL При разработке формата XML особое внимание уделялось поддержке различных кодировок символов. Для указания того, какая кодировка была использована используется заголовок XML-документа. Пример: <?xml version=1.0" encoding=Windows-1251"?> Если кодировка указана не была, то по умолчанию предполагается кодировка UTF-8. На XML-парсер возложена обязанность корректно прочитать заголовок и использовать соответствующую кодировку для получения Unicode-символов. Разные парсеры могут поддерживать разные наборы кодировок, но UTF-8 обязаны поддерживать все. Здесь также, как и в случае с JavaMail наименования кодировок, описанные в стандарте XML могут расходится с наименованиями, принятыми в Java. Разные парсеры по разному выходят из положения. Crimson просто использует некоторое кол-во дополнительных синонимов, а в остальном полагается на синонимы кодировок из Java. Xerces же по умолчанию использует внутреннюю таблицу (класс org.apache.xerces.readers.MIME2Java), а если не находит там кодировку, то бросает исключение о неподдерживаемой кодировке. В Xerces версии 1.4.0 русских кодировок там всего две - KOI8-R и ISO-8859-5. Однако это поведение по умолчанию можно изменить при помощи разрешения у парсера специального feature "http://apache.org/xml/features/allow-java-encodings". Если этот feature разрешён (при помощи метода setFeature()), то парсер после поиска в таблице будет пытаться использовать стандартный Java-вский механизм и соответственно Java-вский набор кодировок. В случае использования интерфейса SAX сделать это можно таким, например, образом (при использовании JAXP): SAXParserFactory parserFactory = SAXParserFactory.newInstance(); SAXParser parser = parserFactory.newSAXParser(); parser.getXMLReader().setFeature("http://apache.org/xml/features/allow-java-encodings",true); Для DOM, к сожалению, подобного механизма feature-ов не предусмотрено, но можно вместо JAXP для создания DOM напрямую использовать класс org.apache.xerces.parsers.DOMParser, у которого уже есть метод setFeature(). Если же Xerces используется не напрямую, а посредством другого пакета, то необходимо настроить этот пакет дабы он сам выставлял этот feature. Если же такой возможности не предусмотрено, то остаётся только один выход - править ручками. Для этого можно или подправить список кодировок в классе org.apache.xerces.readers.MIME2Java или установить указанный feature как true по умолчанию. Для чтения документа XML из потока данных обычно используется класс org.xml.sax.InputSource. Собственно сам поток может быть представлен или в виде байтового потока (java.io.InputStream) или в виде потока символов (java.io.Reader). Соответственно ответственность за корректное распознавание кодировки возлагается или на парсер или на того, кто создаёт объект Reader. У класса InputSource есть так же метод setEncoding(), при помощи которого можно явно задать кодировку в случае использования потока байтов. Работает это всё таким образом: Если был задан поток символов (Reader), то он будет использован для чтения данных. Кодировка, установленная методом setEncoding() при этом игнорируется, как игнорируется и кодировка, указанная в заголовке XML-документа. Если вместо потока символов был задан поток байтов (InputStream), то используется он. Если установлена кодировка методом setEncoding(), то используется она, а если нет - то парсер использует кодировку, указанную в заголовке XML-документа. Если при чтении заголовка XML-документа обнаруживается расхождение между заданной кодировкой и кодировкой из заголовка, то парсеры могут поступать по разному. Crimson, например, при этом выдаёт предупреждение, а Xerces молча пропускает. С чтением XML-документов мы разобрались, теперь перейдём к их созданию. Единого стандарта на создание документов, в отличии от чтения, пок...
Notice: Undefined offset: 1 in /home/area7ru/area7.ru/docs/linkmanager/links.php on line 21 |