Как позволить синтаксическому анализатору SAX определять кодировку из объявления xml?

Я пытаюсь анализировать файлы xml из разных источников (над которыми я мало контролирую). Большинство из них закодированы в UTF-8 и не вызывают никаких проблем с использованием следующего фрагмента:

SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
FeedHandler handler = new FeedHandler();
InputSource is = new InputSource(getInputStream());
parser.parse(is, handler);

Поскольку SAX по умолчанию использует UTF-8, это нормально. Однако некоторые документы заявляют:

<?xml version="1.0" encoding="ISO-8859-1"?>

Несмотря на то, что ISO-8859-1 объявлен, SAX по-прежнему использует UTF-8 по умолчанию. Только если я добавлю:

is.setEncoding("ISO-8859-1");

Будет ли SAX использовать правильную кодировку.

Как я могу позволить SAX автоматически определять правильную кодировку из объявления xml без моей специальной настройки? Мне это нужно, потому что я не знаю заранее, какой будет кодировка файла.

Заранее спасибо, Аллан


person Allan    schedule 14.08.2010    source источник


Ответы (2)


Используйте InputStream в качестве аргумента для InputSource, если вы хотите, чтобы Sax автоматически определял кодировку.

Если вы хотите установить определенную кодировку, используйте Reader с указанной кодировкой или setEncoding.

Почему? Поскольку для алгоритмов кодирования автоопределения требуются необработанные данные, а не преобразованные в символы.

Вопрос в теме: Как позволить синтаксическому анализатору SAX определять кодировку из объявления xml? Я нашел ответ Аллана на вопрос вводящим в заблуждение и предоставил альтернативный, основанный на комментарии Йорна Хорстманна и моих более поздний опыт.

person Jarekczek    schedule 04.09.2012
comment
Да: ключевым моментом является то, что SAX обнаружит кодировку <?xml encoding=''?> PI только, если InputSource сконструирован из экземпляра InputStream; он не будет работать, если построен из Reader (потому что суть Reader в том, что его вывод является «пост-декодированием»). То есть: new InputSource(getInputStream()) правильно. - person Norman Gray; 03.07.2014
comment
Кстати, есть ли какая-нибудь библиотека, которая анализирует только объявление XML, используя приведенные выше алгоритмы? Я спрашиваю, потому что я не могу использовать Sax напрямую, но я хотел бы извлечь информацию о кодировке из моего xmls. - person Andrea Richiardi; 02.10.2014
comment
Это должно быть приемлемое решение. InputStream не имеет информации о кодировке, поэтому SAX определяет кодировку, пытаясь прочитать атрибут кодировки из файла XML. Это также работает при работе с XsltTransformer. - person phobic; 25.08.2016
comment
Есть ли возможность получить точное содержимое атрибутивной кодировки пролога xml? Локатор Xerces не работает. - person Kuronashi; 20.12.2019

Я сам нашел ответ.

Парсер SAX использует InputSource внутри и из документации InputSource:

Анализатор SAX будет использовать объект InputSource, чтобы определить, как читать ввод XML. Если доступен поток символов, синтаксический анализатор будет читать этот поток напрямую, игнорируя любое объявление кодировки текста, найденное в этом потоке. Если нет потока символов, но есть поток байтов, синтаксический анализатор будет использовать этот поток байтов, используя кодировку, указанную в InputSource, или иначе (если кодировка не указана) автоматически определяет кодировку символов с помощью алгоритма, такого как тот, что в спецификация XML. Если ни символьный поток, ни байтовый поток недоступны, синтаксический анализатор попытается открыть соединение URI с ресурсом, идентифицированным системным идентификатором.

Итак, в основном вам нужно передать поток символов синтаксическому анализатору, чтобы он мог подобрать правильную кодировку. См. Решение ниже:

SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
FeedHandler handler = new FeedHandler();
Reader isr = new InputStreamReader(getInputStream());
InputSource is = new InputSource();
is.setCharacterStream(isr);
parser.parse(is, handler);
person Allan    schedule 14.08.2010
comment
При создании InputStreamReader без указания кодировки будет использоваться кодировка по умолчанию вашего компьютера, которая, вероятно, является iso-8859-1. Как вы процитировали, объявление кодировки в xml будет игнорироваться при использовании потока символов, поэтому этот код будет работать только с документами iso-8859-1. Ваш исходный код действительно должен был работать, возможно, вы могли бы добавить исключение или точную проблему, которую вы видите, в свой вопрос. При использовании байтового потока без установки кодировки на InputSource анализатор xml должен автоматически определять кодировку, как описано в w3.org/TR/REC-xml/#sec-guessing. - person Jörn Horstmann; 14.08.2010
comment
Обычно я получаю исключение недопустимого токена, если не использую is.setCharacterStream (). - person Allan; 16.08.2010
comment
Возможно, это сработало для вас, но Йорн прав. Документация, на которую вы ссылались, актуальна и верна. И это говорит вам, что исходный код с InputStream был правильным. Ошибка в самом документе. Если вы используете обходной путь, такой как переопределение кодировки или ее автоматическое определение каким-либо другим способом, кроме спецификации XML, как вы делаете с InputStreamReader, вы должны задокументировать этот факт. - person John Watts; 21.06.2012