Главная Случайная страница


Полезное:

Как сделать разговор полезным и приятным Как сделать объемную звезду своими руками Как сделать то, что делать не хочется? Как сделать погремушку Как сделать так чтобы женщины сами знакомились с вами Как сделать идею коммерческой Как сделать хорошую растяжку ног? Как сделать наш разум здоровым? Как сделать, чтобы люди обманывали меньше Вопрос 4. Как сделать так, чтобы вас уважали и ценили? Как сделать лучше себе и другим людям Как сделать свидание интересным?


Категории:

АрхитектураАстрономияБиологияГеографияГеологияИнформатикаИскусствоИсторияКулинарияКультураМаркетингМатематикаМедицинаМенеджментОхрана трудаПравоПроизводствоПсихологияРелигияСоциологияСпортТехникаФизикаФилософияХимияЭкологияЭкономикаЭлектроника






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-документов мы разобрались, теперь перейдём к их созданию. Единого стандарта на создание документов, в отличии от чтения, пока нет. Предполагается, что, следующая версия рекомендаций комитета W3C будет включать в себя и создание документов, но пока что создатели парсеров делают кто во что горазд.

В случае с Crimson сохранить созданный документ DOM можно при помощи метода write() у класса org.apache.crimson.tree.XmlDocument. В качестве аргумента можно передать или поток символов (Writer) или поток байтов (OutputStream). Вместе с потоком можно передать и необходимую кодировку. Если использован поток байтов, а кодировка указана не была, то используется UTF-8. Если использован поток символов вместе с именем кодировки, то имя используется только для записи в заголовок документа. Если Writer передан без кодировки, то делается проверка - если это экземляр OutputStreamWriter, то для выяснения что писать в заголовок зовётся его метод getEncoding(). Если же это другой Writer, то кодировка в заголовок записана не будет, что по стандарту означает кодировку UTF-8. Пример:

XmlDocument doc =...;

OutputStream os =...;

doc.write(os,"Windows-1251");

В Xerces для создания документов используются классы из пакета org.apache.xml.serialize. Собственно для записи используется класс XMLSerializer, а для настройки выходного формата - класс OutputFormat. В конструкторе XMLSerializer можно передавать как потоки байтов, так и потоки символов. В случае потоков символов используемая кодировка должна совпадать с заданной в OutputFormat. Важно не забыть задать используемую кодировку в OutputFormat - в противном случае русские буквы будут представлены в виде кодов, типа такого: "&#x410;&#x411;&#x412;" для символов "АБВ". Пример:

OutputStream os =...;

OutputFormat format = new OutputFormat(Method.XML, "Windows-1251", true)

XMLSerializer serializer = new XMLSerializer(os,format);

serializer.serialize(doc);

Castor XML

Пакет Castor предназначен для решения проблем долговременного хранения объектов. В числе прочего он содержит в себе подсистему Castor XML, которая по сути дела является надстройкой над XML-парсером и позволяет автоматизировать чтение и запись XML-файлов. Castor XML по умолчанию использует парсер Xerces, поэтому проблемы Xerces перекочёвывают и сюда. В документации к Castor в примерах используются потоки символов (Reader и Writer), а это может привести к рассогласованности между используемой в потоке кодировки и реальной кодировки XML-файла. Как уже говорилось выше, чтобы прочитать при помощи Xerces XML-файл в произвольной кодировке нужно, во первых, использовать потоки байтов, а во вторых, установить специальный feature. К счастью эта возможность предусмотрена в Castor. Для этого нужно скопировать файл castor.properties (взять его можно из каталога org\exolab\castor в файле castor-0.9.3-xml.jar) в подкаталог lib в JRE, и установить там переменную org.exolab.castor.sax.features. Пример:

# Comma separated list of SAX 2 features that should be enabled

# for the default parser.

#

#org.exolab.castor.features=

org.exolab.castor.sax.features=http://apache.org/xml/features/allow-java-encodings

Стоит отметить, что по умолчанию там стоит переменная org.exolab.castor.features, но это, очевидно, опечатка - если посмотреть в исходники, то там анализируется org.exolab.castor.sax.features (это справедливо для Castor версии 0.9.3 от 03.07.2001). Пример чтения с использованием потоков байтов:

public static Object load(Class cls, String mappingFile, InputStream is)

throws Exception

{

Mapping mapping = loadMapping(cls,mappingFile);

Unmarshaller unmarshaller = new Unmarshaller(cls);

unmarshaller.setMapping(mapping);

return unmarshaller.unmarshal(new InputSource(is));

}

Для создания XML-файлов необходимо правильно указать формат для Xerces. Пример:

public static void save(Object obj, String mappingFile, OutputStream os, String encoding)

throws Exception

{

Mapping mapping = loadMapping(obj.getClass(),mappingFile);

try

{

XMLSerializer serializer = new XMLSerializer(os,new OutputFormat(Method.XML, encoding, true));

Marshaller marshaller = new Marshaller(serializer);

marshaller.setMapping(mapping);

marshaller.marshal(obj);

}

finally { os.flush(); }

}

Для загрузки файлов маппинга в этих примерах можно использовать такой код:

private static Mapping loadMapping(Class cls,String mappingFile)

throws Exception

{

ClassLoader loader = cls.getClassLoader();

 

Mapping mapping = new Mapping(loader);

mapping.loadMapping(new InputSource(loader.getResourceAsStream(mappingFile)));

return mapping;

}

XSL

Спецификация XSL описывает стандарт на преобразование XML-документов. Когда при помощи XSL выполняется преобразование из одного XML-документа в другой, особых причин для беспокойства нет - и тот и другой являются Unicode-документами, поэтому нет преобразований из символов в байты и обратно, могущих повлиять на результат. Другое дело, когда выполняется преобразование из XML в HTML или вообще в текстовый файл. Формат выходного файла задаётся настройкой тега xsl:output, в котором можно задать используемую кодировку. Пример:

<xsl:output encoding="Windows-1251" method='html' indent='yes'/>

Если XSLT-процессор не знает указанной кодировки, то он должен или выдать ошибку или использовать UTF-8 (или UTF-16). Если формируется HTML, то XSLT-процессор должен добавить тег meta, в котором будет указана реально использованная кодировка:

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

Всё бы хорошо, но некоторые XSLT-процессоры не поддерживают данный тег (по спецификации они и не обязаны). В частности пакет Cocoon его не поддерживает, т.к. по словам разработчиков он противоречит внутренней архитектуре этого пакета. Вместо этого там поддерживается указание выходного формата при помощи инструкции препроцессора cocoon-format. Пример вставки этой инструкции в XSL:

<xsl:processing-instruction name="cocoon-format">

type="text/html"

</xsl:processing-instruction>

Таким образом можно динамически менять выходной формат. Если это не требуется, то можно записать инструкцию и статически (в исходном XML-документе):

<?cocoon-format type="text/html"?>

Собственно используемая кодировка настраивается для каждого формата отдельно в файле cocoon.properties.

Новая версия Cocoon 2.0 кроме управления кодировками позволяет сделать в плане локализации уже гараздо больше. Подробности Вы можете узнать на их сайте.

В случае использования JAXP для генерации выходного потока (пакет javax.xml.transform) кроме использования тега xsl:output можно использовать методы setOutputProperty объекта Transformer. Пример сохранения документа в нужной кодировке:

TransformerFactory trFactory = TransformerFactory.newInstance();

Transformer transformer = trFactory.newTransformer();

transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, docPublic);

transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, docSystem);

transformer.setOutputProperty(OutputKeys.INDENT, "yes");

transformer.setOutputProperty(OutputKeys.ENCODING, encoding);

OutputStream os =...;

StreamResult result = new StreamResult(os);

transformer.transform(source, result);

Тут есть один подводный камень - реализация Transformer должна поддерживать нужную кодировку. Xalan из состава JDK 1.4.0_x и 1.4.1_x поддерживает только две русские кодировки - KOI8-R и ISO-8859-5. Если хочется использовать Windows-1251, то можно воспользоваться механизмом endorsed:

Создаёте каталог %JAVA_HOME%\jre\lib\endorsed

Копируете туда jar с пропатченым классом: XalanRusChars.jar

В JDK 1.4.2 Beta включена новая версия Xalan, которая вроде как уже поддерживает кодировку 1251.

FOP

Пакет FOP предназначен для обработки документов по стандарту XSL FO (Formating Objects). В частности он позволяет создавать PDF-документы на базе документов XML. Для преобразования из исходного XML в FO пакет FOP по умолчанию использует XSLT-процессор Xalan в паре с Xerces. Для создания итогового изображения в FOP необходимо подключить шрифты, поддерживающие русские буквы. Вот как можно проделать это для версии 0.20.1:

В подкаталог conf\fonts (например, в c:\fop-0.20.1\conf\fonts\) скопировать файлы ttf из системного каталога Windows. Для Arial normal/normal, normal/bold, italic/normal и italic/bold нужны файлы arial.ttf, arialbd.ttf, ariali.ttf и arialbi.ttf.

Сгенерировать файлы описаний шрифтов (типа arial.xml). Для этого для каждого шрифта нужно выполнить команду (это для Arial normal/normal, всё в одну строку):

java -cp.;c:\fop-0.20.1\build\fop.jar;c:\fop-0.20.1\lib\batik.jar;

c:\fop-0.20.1\lib\xalan-2.0.0.jar;c:\fop-0.20.1\lib\xerces.jar;

c:\fop-0.20.1\lib\jimi-1.0.jar

org.apache.fop.fonts.apps.TTFReader fonts\arial.ttf fonts\arial.xml

В FOP добавить в conf/userconfig.xml описание шрифта с русскими буквами, типа:

<font metrics-file="c:\fop-0.20.1\conf\fonts\arial.xml" kerning="yes"

embed-file="c:\fop-0.20.1\conf\fonts\arial.ttf">

<font-triplet name="Arial" style="normal" weight="normal"/>

<font-triplet name="ArialMT" style="normal" weight="normal"/>

</font>

Аналогично добавляются Arial normal/bold, italic/normal и italic/bold.

При вызове FOP из командной строки после org.apache.fop.apps.Fop писать -c c:\fop-0.20.1\conf\userconfig.xml Если нужно использовать FOP из сервлета, то нужно в сервлете после строчки

Driver driver = new Driver();

добавить строчки:

// Каталог fonts (c:\weblogic\fonts) был создан исключительно для удобства.

String userConfig = "fonts/userconfig.xml";

File userConfigFile = new File(userConfig);

Options options = new Options(userConfigFile);

Тогда расположение файлов ttf в файле userconfig.xml можно указать относительно корня сервера приложения, без указания абсолютного пути:

<font metrics-file="fonts/arial.xml" kerning="yes"

embed-file="fonts/arial.ttf">

<font-triplet name="Arial" style="normal" weight="normal"/>

<font-triplet name="ArialMT" style="normal" weight="normal"/>

</font>

В файле FO (или XML и XSL) перед использованием шрифта писать:

font-family="Arial"

font-weight="bold" (Если используется Arial bold)

font-style="italic" (Если используется Arial italic)

Данный алгоритм прислал Алексей Тюрин, за что ему отдельное спасибо.

Если Вы используете встроенный в FOP просмотрщик, то необходимо учесть его особенности. В частности, хотя предполагается, что надписи в нём русифицированы, на самом деле сделано это с ошибкой (в версии 0.19.0). Для загрузки надписей из файлов ресурсов в пакете org.apache.fop.viewer.resources используется собственный загрузчик (класс org.apache.fop.viewer.LoadableProperties). Кодировка чтения там жёстко зафиксирована (8859_1, как и в случае Properties.load()), однако поддержка записи вида "\uXXXX" не реализована. Я сообщил об этой ошибке разработчикам, они включили её исправление в свои планы.

Кроме всего прочего существует сайт посвящённый русификации FOP (http://www.openmechanics.net/rusfop/) Там Вы сможете найти дистрибутив FOP с уже исправленными ошибками и подключенными русскими шрифтами.

POI

Пакет Jakarta POI предназначен для работы с документами Microsoft Office. Пока что более-менее работающей там является только поддержка файлов MS Excel (xls). Особой сложности в работе с русским языком нет, но надо учитывать нюанс, что для работы с ячекой используется класс org.apache.poi.hssf.usermodel.Cell, у которого есть метод setEncoding(short encoding), однако вместо привычных "Cp1255" и "Cp866", необходимо исользовать константы ENCODING_COMPRESSED_UNICODE (0) и ENCODING_UTF_16 (1). По умолчанию включен первый режим, а для нормальной работы с русским языком необходимо использовать ENCODING_UTF_16. Причем что самое важное, эту установку необходимо выполнять для каждой, создаваемой ячейки. Пример кода:

HSSFWorkbook wb = new HSSFWorkbook();

HSSFSheet sheet = wb.createSheet("Sheet1");

HSSFRow row = sheet.createRow((short)0);

for(int i = 0; i < 10; i++)

{

HSSFCell cell = row.createCell((short)i);

cell.setEncoding((short)cell.ENCODING_UTF_16);

cell.setCellValue("Тест русского языка");

}

Создать лист с названием содержащим русские символы, к сожалению, не удаётся. Данное описание прислал Вячеслав Яковенко, за что ему отдельное спасибо.

CORBA

В стандарте CORBA предусмотрен тип, соответствующий Java-овскому типу String. Это тип wstring. Всё бы хорошо, но некоторые CORBA-сервера не поддерживают его в полной мере. Типичные исключения, возникающие при спотыкании на русских буквах: org.omg.CORBA.MARSHAL: minor code 5 completed No или org.omg.CORBA.DATA_CONVERSION. Лучше всего, конечно, заменить CORBA-сервер. К сожалению у меня нет статистики, поэтому я не могу сказать, с какими проблем не будет. Если сменить систему не представляется возможным, можно вместо типа wstring использовать тип string в паре с нашим любимым преобразованием:

// Серверная часть

a = new Answer(new String(src.getBytes("Cp1251"),"ISO-8859-1"));

...

// Клиентская часть

Answer answer=serverRef.getAnswer();

res = new String(answer.msg.getBytes("ISO-8859-1"),"Cp1251");

Тип wstring при этом лучше не использовать, потому как тем самым Вы кривость сервера будете компенсировать кривостью своих компонентов, а это практически всегда чревато разнообразными проблемами в будущем.

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

Честно говоря, не лежит у меня душа к таким решениям, ну да что поделаешь, иногда оно единственное.

JNI

JNI (Java Native Interface) - это стандарт по взаимодействию с C/C++-ным кодом. Как и следовало ожидать, на этом водоразделе тоже происходит столкновение байтов и символов. Большинство C/C++-ных программ пишется без учёта Unicode, многие программисты даже не знают о нём. Я сам, за 7 лет писательства на C/C++, пока не начал писать на Java, про Unicode знал только по наслышке. Большинство строковых операций в C/C++ сделаны для 8-битового сишного типа char. В принципе, есть некоторые подвижки в этом направлении, в частности для Windows NT можно откомпилировать код, который будет взаимодействовать с Unicode-вариантами Win32 API, но, к сожалению, этого часто недостаточно.

Таким образом главная задача - получить тип char* из типа jstring (JNI-шное отображение String) и наоборот. Практически во всех описаниях и примерах JNI для этого используется пара функций GetStringUTFChars()/ReleaseStringUTFChars(). Коварные буржуины и здесь приготовили засаду - эти функции формируют массив байтов по стандарту UTF, который соответствует ожидаемому только для ASCII-символов (первых 128 значений). Русские буквы опять в пролёте. Сишные строки char* очень хорошо ложатся на Java-овский тип byte[], но при этом возникает загвоздка в виде ноль-символа. Его нужно добавлять при преобразовании byte[]->char* и учитывать при обратном преобразовании. Пример:

public void action(String msg) throws java.io.IOException

{

int res = nAction(msg);

if(res!=0) throw new java.io.IOException(nGetErrorString(res));

}

private native int nAction(String msg);

private native String nGetErrorString(int error);

...

jbyteArray getStringBytes(JNIEnv *env, jstring str)

{

if(!str) return NULL;

jmethodID getBytes = env->GetMethodID(env->GetObjectClass(str),"getBytes","()[B");

jbyteArray buf = (jbyteArray)env->CallObjectMethod(str,getBytes);

if(!buf) return NULL;

// Добавляем ноль-символ

jsize len = env->GetArrayLength(buf);

jbyteArray nbuf = env->NewByteArray(len+1);

if(len!=0)

{

jbyte *cbuf = env->GetByteArrayElements(buf,NULL);

env->SetByteArrayRegion(nbuf,0,len,cbuf);

env->ReleaseByteArrayElements(buf,cbuf,JNI_ABORT);

}

env->DeleteLocalRef(buf);

return nbuf;

}

JNIEXPORT jint JNICALL Java_Test_nAction

(JNIEnv *env, jobject obj, jstring msg)

{

jbyteArray bmsg = getStringBytes(env,msg);

if(!bmsg) return -1;

jbyte *cmsg = env->GetByteArrayElements(bmsg,NULL);

printf(cmsg);

jint res = do_something(cmsg);

env->ReleaseByteArrayElements(bmsg,cmsg,JNI_ABORT);

return res;

}

jstring newString(JNIEnv *env, jbyteArray jbuf, int len)

{

jclass stringClass = env->FindClass("java/lang/String");

if(!stringClass) return NULL;

jmethodID init = env->GetMethodID(stringClass,"","([BII)V");

if(!init) return NULL;

return (jstring)env->NewObject(stringClass,init,jbuf,0,len);

}

jstring newString(JNIEnv *env, const char *buf)

{

if(!buf) return NULL;

 

int bufLen = strlen(buf);

if(bufLen==0)

{

return env->NewString((const jchar *)L"", 0);

}

jbyteArray jbuf = env->NewByteArray(bufLen);

if(!jbuf) return NULL;

env->SetByteArrayRegion(jbuf,0,bufLen,(jbyte*)buf);

jstring jstr = newString(env,jbuf,bufLen);

env->DeleteLocalRef(jbuf);

return jstr;

}

JNIEXPORT jstring JNICALL Java_Test_nGetErrorString

(JNIEnv *env, jobject obj, jint error)

{

char cmsg[256];

memset(cmsg,0,sizeof(cmsg));

get_error_string(error,cmsg,sizeof(cmsg));

return newString(env,cmsg);

}

Тут используется преобразование символов по умолчанию, что вполне естественно при взаимодействиях с системным API. Если же Вам необходима определённая кодовая страница, соответственно нужно добавить её название.

GUI (AWT, Swing)

Многие связывают неправильный вывод русских букв с неправильной установкой шрифта. На самом деле в Java всё сложнее и редко действительно связанно со шрифтами.

Где же действительно лежат наибольшие подводные камни? В основном это связанно с неправильной перекодировкой символов. Часть этих проблем и методы их решения описаны выше. Если у Вас все преобразования выполняются корректно, и для вывода используется шрифт Unicode, то есть очень большой шанс, что Ваша программа будет работать правильно.

Если проблемы всё же остались, тут нужно выяснить, где они возникают. Попробуйте запустить приложение под разными JVM, под разными платформами, на разных броузерах. Пример достаточно универсального алгоритма поиска проблем предложен ниже, в разделе Типичные ошибки.

Если программа не работает нигде - значит проблема только в ней и в Ваших руках. Внимательно перечитайте всё, что было написано выше, и ищите. Если же проблема проявляется только в конкретном окружении - значит дело, возможно в настройках. Где именно - зависит от того, какой графической библиотекой Вы пользуетесь. Если AWT - помочь может правильная настройка файла font.properties.ru. Пример корректного файла можно взять из Java 2. Если у Вас нет этой версии, можете скачать его с данного сайта: версия для Windows, версия для Linux (см. также раздел по Linux ниже). Этот файл задаёт используемые шрифты и кодовые страницы. Если у Вас установлена русская версия OS - просто добавьте этот файл туда, где лежит файл font.properties. Если же это англицкая версия, то нужно, или переписать этот файл вместо font.properties или дополнительно сменить текущие региональные настройки на русские. Иногда может сработать настройка -Duser.language=ru, но чаще - нет. Тут примерно те же проблемы, что и с file.encoding - сработает или нет, зависит от JDK (см. ошибку за номером 4152725).

Если кроме русских букв Вам также надо выводить, к примеру, греческие, то обычно достаточно просто правильно указать их кода. Работает всё это примерно таким способом:

По умолчанию в AWT и Swing используются виртуальные шрифты, настраиваемые в font.properties.ru (dialog, dialoginput и т.д.). Эти шрифты виртуальные и при выводе, в зависимости от кода выводимого символа используется один из реальных шрифтов. Например, вот эти строчки:

dialog.0=Arial,RUSSIAN_CHARSET

dialog.1=WingDings,SYMBOL_CHARSET,NEED_CONVERTED

dialog.2=Symbol,SYMBOL_CHARSET,NEED_CONVERTED

задают, что виртуальный шрифт dialog обычного начертания состоит из 3-х шрифтов (Arial, WingDings и Symbol). Далее, вот эти строчки:

fontcharset.dialog.0=sun.io.CharToByteCp1251

fontcharset.dialog.1=sun.awt.windows.CharToByteWingDings

fontcharset.dialog.2=sun.awt.CharToByteSymbol

задают, какие классы нужно использовать для перекодирования из Unicode в кодировку данного шрифта. При выводе символов сначала ищется, в каком шрифте определены выводимые символы. Это определяется тем, какие символы может конвертировать указанные классы. Есть так же дополнительная настройка (exclusion), которая явно задаёт диапазоны символов, которые неприменимы для данного шрифта. Например, вот эта строка

exclusion.dialog.0=0100-0400,0460-ffff

задаёт, что при выводе символов с кодами от 0100 до 0400 и от 0460 до ffff шрифт 0 (Arial) использовать не следует. Эта строка нужна, в основном, для оптимизации.

Таким образом, при выводе греческих символов шрифт 0 (Arial) не подходит по exclusion, шрифт 1 (WingDings) не подходит, т.к. в таблице перекодировки CharToByteWingDings они отсутствуют поэтому используется шрифт 2 (Symbol), в котором есть греческие символы.

С библиотекой Swing всё проще - в ней всё рисуется через подсистему Java2D. Надписи в стандартных диалогах (JOptionPane, JFileChooser, JColorChooser) переделать на русский очень просто - достаточно лишь создать несколько файлов ресурсов. Я это уже проделал, так что можете просто взять готовый файл и добавить его в lib\ext или в CLASSPATH. Единственная проблема, с которой я столкнулся - в версиях JDK начиная с 1.2 rc1 и по 1.3 beta, русские буквы не выводятся под Win9x при использовании стандартных шрифтов (Arial, Courier New, Times New Roman, etc.) из-за ошибки в Java2D. Ошибка весьма своеобразна - со стандартными шрифтами изображения букв отображаются не в соответствии с кодами Unicode, а по таблице Cp1251 (кодировка Ansi). Эта ошибка зарегистрирована в BugParade под номером 4192443. По умолчанию в Swing используются шрифты, задаваемые в файле font.properties.ru, так что достаточно заменить их другими - и русские буквы появляются. К сожалению, набор рабочих шрифтов небольшой - это шрифты Tahoma, Tahoma Bold и два набора шрифтов из дистрибутива JDK - Lucida Sans * и Lucida Typewriter * (пример файла font.properties.ru). Чем эти шрифты отличаются от стандартных - мне непонятно.

Начиная с версии 1.3rc1 эта проблема уже исправлена, так что нужно просто обновить JDK. JDK 1.2 уже сильно устарел, так что я не рекомендую им пользоваться. Так же надо учесть, что с оригинальной версией Win95 поставляются шрифты, не поддерживающие Unicode - в этой ситуации можно просто скопировать шрифты из Win98 или WinNT.

Типичные ошибки, или "куда делась буква Ш?"

Буква Ш.

Этот вопрос ("куда делась буква Ш?") довольно часто возникает у начинающих программистов на Java. Давайте разберёмся, куда же она действительно чаще всего девается.:-)

Вот типичная программа а-ля HelloWorld:

public class Test

{

public static void main(String[] args)

{

System.out.println("ЙЦУКЕНГШЩЗХЪ");

}

}

в Far-е сохраняем данный код в файл Test.java, компиляем...

C:\>javac Test.java

и запускаем...

C:\>java Test

ЙЦУКЕНГ?ЩЗХЪ

Что же произошло? Куда делась буква Ш? Весь фокус здесь в том, что произошла взаимокомпенсация двух ошибок. Текстовый редактор в Far по умолчанию создаёт файл в DOS-кодировке (Cp866). Компилятор же javac для чтения исходника использует file.encoding (если не указано иное ключиком -encoding). А в среде Windows с русскими региональными настройками кодировкой по умолчанию является Cp1251. Это первая ошибка. В результате, в скомпилированном файле Test.class символы имеют неверные кода. Вторая ошибка состоит в том, что для вывода используется стандартный PrintStream, который тоже использует настройку из file.encoding, однако консольное окно в Windows отображает символы, используя кодировку DOS. Если бы кодировка Cp1251 была взаимоодназначной, то потери данных бы не было. Но символ Ш в Cp866 имеет код 152, который в Cp1251 не определён, и поэтому отображается на Unicode-символ 0xFFFD. Когда происходит обратное преобразование из char в byte, вместо него подставляется символ '?'.

На аналогичную компенсацию можно нарваться, если прочитать символы из текстового файла при помощи java.io.FileReader, а затем вывести их на экран через System.out.println(). Если файл был записан в кодировке Cp866, то вывод будет идти верно, за исключением опять же буквы Ш.

Прямая конверсия byte<->char.

Эта ошибка является любимой у зарубежных программистов на Java. Она довольно подробно рассмотрена в начале описания. Если Вы когда-нибудь будете смотреть чужие исходники, то всегда обращайте внимание на явную конверсию типов - (byte) или (char). Довольно часто в таких местах закопаны грабли.

Алгоритм поиска проблем с русскими буквами

Если Вы не представляете себе где в Вашей программе может происходить потеря русских букв, то можно попробовать следующий тест. Любую программу можно рассматривать как обработчик входных данных. Русские буквы - это такие же данные, они проходят в общем случае три стадии обработки: они откуда-то читаются в память программы (вход), обрабатываются внутри программы и выводятся пользователю (выход). Для того, чтобы определить место проблем, надо попробовать вместо данных зашить в исходник такую тестовую строку: "АБВ\u0410\u0411\u0412", и попробовать её вывести. После этого смотрите, что у Вас вывелось:

Если Вы увидите "АБВАБВ", значит компиляция исходников и вывод у Вас работают правильно.

Если Вы увидите "???АБВ" (или любые другие символы кроме "АБВ" на месте первых трёх букв), значит вывод работает правильно, но вот компиляция исходников происходит неверно - скорей всего не указан ключик -encoding.

Если Вы увидите "??????" (или любые другие символы кроме "АБВ" на месте второй тройки букв), значит вывод у Вас работает неверно.

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

Об утилите native2ascii

Эта утилита входит в состав Sun JDK и предназначена для преобразования исходных текстов к ASCII-виду. Она читает входной файл, используя указанную кодировку, а на выходе записывает символы в формате "\uXXXX". Если указать ключик -reverse, то выполняется обратная конвертация. Эта программа очень полезна для конвертации файлов ресурсов (.properties) или для обработки исходников, если Вы предполагаете, что они могут компиляться на компьютерах с отличными от русских региональными настройками.

Если запустить программу без параметров, она работает со стандартным входом (stdin), а не выводит подсказку по ключам, как остальные утилиты. Это приводит к тому, что многие и не догадываются о необходимости указания параметров (кроме, может быть, тех, кто нашёл в себе силы и мужество заглянуть таки в документацию:-). Между тем этой утилите для правильной работы необходимо, как минимум, указать используемую кодировку (ключик -encoding). Если этого не сделать, то будет использована кодировка по умолчанию (file.encoding), что может несколько расходится с ожидаемой. В результате, получив неверные кода букв (из-за неверной кодировки) можно потратить весьма много времени на поиск ошибок в абсолютно верном коде.

Date: 2015-10-18; view: 504; Нарушение авторских прав; Помощь в написании работы --> СЮДА...



mydocx.ru - 2015-2024 year. (0.006 sec.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав - Пожаловаться на публикацию