Подтвердить что ты не робот

Преобразование UTF-8 в ISO-8859-1 в Java

Я читаю XML-документ (UTF-8) и в конечном итоге показываю содержимое на веб-странице, используя ISO-8859-1. Как и ожидалось, несколько символов отображаются неправильно, например ", и (они отображаются как?).

Можно ли преобразовать эти символы из UTF-8 в ISO-8859-1?

Вот фрагмент кода, который я написал для этого:

BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
StringBuilder sb = new StringBuilder();

String line = null;
while ((line = br.readLine()) != null) {
  sb.append(line);
}
br.close();

byte[] latin1 = sb.toString().getBytes("ISO-8859-1");

return new String(latin1);

Я не совсем уверен, что происходит, но я считаю, что readLine() вызывает горю (поскольку строки будут закодированы в формате Java/UTF-16?). Другой вариант, который я пытался, заключался в замене latin1 на

byte[] latin1 = new String(sb.toString().getBytes("UTF-8")).getBytes("ISO-8859-1");

Я прочитал предыдущие сообщения по этому вопросу, и я учусь, когда иду. Заранее благодарим за помощь.

4b9b3361

Ответ 1

Я не уверен, есть ли нормализация в стандартной библиотеке, которая сделает это. Я не думаю, что преобразование "умных" котировок обрабатывается стандартными Unicode normalizer, но не цитируйте меня.

Умное дело - сбросить ISO-8859-1 и начать использовать UTF-8. Тем не менее, можно кодировать любую нормально разрешенную кодовую точку Юникода в HTML-страницу, закодированную как ISO-8859-1. Вы можете кодировать их с помощью escape-последовательностей, как показано ниже:

public final class HtmlEncoder {
  private HtmlEncoder() {}

  public static <T extends Appendable> T escapeNonLatin(CharSequence sequence,
      T out) throws java.io.IOException {
    for (int i = 0; i < sequence.length(); i++) {
      char ch = sequence.charAt(i);
      if (Character.UnicodeBlock.of(ch) == Character.UnicodeBlock.BASIC_LATIN) {
        out.append(ch);
      } else {
        int codepoint = Character.codePointAt(sequence, i);
        // handle supplementary range chars
        i += Character.charCount(codepoint) - 1;
        // emit entity
        out.append("&#x");
        out.append(Integer.toHexString(codepoint));
        out.append(";");
      }
    }
    return out;
  }
}

Пример использования:

String foo = "This is Cyrillic Ya: \u044F\n"
    + "This is fraktur G: \uD835\uDD0A\n" + "This is a smart quote: \u201C";

StringBuilder sb = HtmlEncoder.escapeNonLatin(foo, new StringBuilder());
System.out.println(sb.toString());

Above, the character LEFT DOUBLE QUOTATION MARK ( U+201C ) is encoded as &#x201C;. A couple of other arbitrary code points are likewise encoded.

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

Ответ 2

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

byte[] latin1 = sb.toString().getBytes("ISO-8859-1");

return new String(latin1);

В Java String/ Char всегда находится в UTF-16BE. Различная кодировка применяется только при преобразовании символов в байты. Предположим, что ваша кодировка по умолчанию - UTF-8, буфер latin1 обрабатывается как UTF-8, а некоторая последовательность латинского-1 может формировать недопустимую последовательность UTF-8, и вы получите?.

Ответ 3

когда вы инициируете свой объект String, вам нужно указать, какую кодировку использовать.

Итак, замените:

return new String(latin1);

return new String(latin1, "ISO-8859-1");

Ответ 4

С Java 8, ответ McDowell можно упростить, как это (при сохранении правильной обработки суррогатных пар):

public final class HtmlEncoder {
    private HtmlEncoder() {
    }

    public static <T extends Appendable> T escapeNonLatin(CharSequence sequence,
                                                          T out) throws java.io.IOException {
        for (PrimitiveIterator.OfInt iterator = sequence.codePoints().iterator(); iterator.hasNext(); ) {
            int codePoint = iterator.nextInt();
            if (Character.UnicodeBlock.of(codePoint) == Character.UnicodeBlock.BASIC_LATIN) {
                out.append((char) codePoint);
            } else {
                out.append("&#x");
                out.append(Integer.toHexString(codePoint));
                out.append(";");
            }
        }
        return out;
    }
}