Удалить не-UTF-8 символов из xml с объявленной кодировкой = utf-8 - Java

Мне нужно обработать этот сценарий в Java:

Я получаю запрос в форме XML от клиента с объявленной кодировкой = utf-8. К сожалению, он может содержать не символы utf-8, и есть требование удалить эти символы из xml на моей стороне (legacy).

Рассмотрим пример, когда этот недопустимый XML содержит £ (фунт).

1) Я получаю xml как java String с E в нем (у меня нет доступа к интерфейсу прямо сейчас, но я, вероятно, получаю xml как строку java). Могу ли я использовать replaceAll (£, ""), чтобы избавиться от этого персонажа? Любые потенциальные проблемы?

2) Я получаю xml как массив байтов - как безопасно обрабатывать эту операцию в этом случае?

Ответы

Ответ 1

1) Я получаю xml как java String с E в нем (у меня нет доступа к интерфейсу прямо сейчас, но я, вероятно, получаю xml как строку java). Могу ли я использовать replaceAll (£, ""), чтобы избавиться от этого персонажа?

Я предполагаю, что вы скорее подразумеваете, что хотите избавиться от символов < ASCII, потому что вы говорите о "старой" стороне. Вы можете избавиться от чего-либо вне печатаемого диапазона ASCII, используя следующее регулярное выражение:

string = string.replaceAll("[^\\x20-\\x7e]", "");

2) Я получаю xml как массив байтов - как безопасно обрабатывать эту операцию в этом случае?

Вам нужно обернуть byte[] в ByteArrayInputStream, чтобы вы могли прочитать их в кодированном символе символа UTF-8 используя InputStreamReader, в котором вы указываете кодировку, а затем используйте BufferedReader, чтобы читать его по строкам.

например.

BufferedReader reader = null;
try {
    reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes), "UTF-8"));
    for (String line; (line = reader.readLine()) != null;) {
        line = line.replaceAll("[^\\x20-\\x7e]", "");
        // ...
    }
    // ...

Ответ 2

UTF-8 - это кодирование; Unicode - это набор символов. Но символ GBP определенно определен в наборе символов Юникода и, следовательно, наиболее определенно представлен в UTF-8.

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

CharsetDecoder utf8Decoder = Charset.forName("UTF-8").newDecoder();
utf8Decoder.onMalformedInput(CodingErrorAction.IGNORE);
utf8Decoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
ByteBuffer bytes = ...;
CharBuffer parsed = utf8Decoder.decode(bytes);
...

Ответ 3

"test text".replaceAll("[^\\u0000-\\uFFFF]", "");

Этот код удаляет все 4-байтные символы utf8 из строки. Это может понадобиться для некоторых целей при выполнении записи Mysql innodb varchar

Ответ 4

Я столкнулся с такой же проблемой при чтении файлов из локального каталога и попробовал это:

BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "UTF-8"));
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document xmlDom = db.parse(new InputSource(in));

Возможно, вам придется использовать сетевой входной поток вместо FileInputStream.

- Капил

Ответ 5

Обратите внимание, что первым шагом должно быть то, что вы спрашиваете создателя XML (который, скорее всего, является источником XML только для генерации XML-данных), чтобы гарантировать правильность их XML перед отправкой вам. Самый простой возможный тест, если они используют Windows, - попросить их просмотреть его в Internet Explorer и увидеть ошибку синтаксического анализа на первом оскорбительном символе.

Пока они исправляют это, вы можете просто написать небольшую программу, которая изменит часть заголовка, чтобы объявить, что кодировка - это ISO-8859-1:

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

а остальные останутся нетронутыми.

Ответ 6

Как только вы преобразуете массив байтов в String на java-машине, вы получите (по умолчанию на большинстве машин) кодированную строку UTF-16. Правильное решение, чтобы избавиться от символов не UTF-8, имеет следующий код:

String[] values = {"\\xF0\\x9F\\x98\\x95", "\\xF0\\x9F\\x91\\x8C", "/*", "look into my eyes 〠.〠", "fkdjsf ksdjfslk", "\\xF0\\x80\\x80\\x80", "aa \\xF0\\x9F\\x98\\x95 aa"};
for (int i = 0; i < values.length; i++) {
    System.out.println(values[i].replaceAll(
                    "[\\\\x00-\\\\x7F]|" + //single-byte sequences   0xxxxxxx
                    "[\\\\xC0-\\\\xDF][\\\\x80-\\\\xBF]|" + //double-byte sequences   110xxxxx 10xxxxxx
                    "[\\\\xE0-\\\\xEF][\\\\x80-\\\\xBF]{2}|" + //triple-byte sequences   1110xxxx 10xxxxxx * 2
                    "[\\\\xF0-\\\\xF7][\\\\x80-\\\\xBF]{3}" //quadruple-byte sequence 11110xxx 10xxxxxx * 3
            , ""));
}

или если вы хотите проверить, содержит ли какая-либо строка символы не utf8, вы должны использовать Pattern.matches, например:

String[] values = {"\\xF0\\x9F\\x98\\x95", "\\xF0\\x9F\\x91\\x8C", "/*", "look into my eyes 〠.〠", "fkdjsf ksdjfslk", "\\xF0\\x80\\x80\\x80", "aa \\xF0\\x9F\\x98\\x95 aa"};
for (int i = 0; i < values.length; i++) {
    System.out.println(Pattern.matches(
                    ".*(" +
                    "[\\\\x00-\\\\x7F]|" + //single-byte sequences   0xxxxxxx
                    "[\\\\xC0-\\\\xDF][\\\\x80-\\\\xBF]|" + //double-byte sequences   110xxxxx 10xxxxxx
                    "[\\\\xE0-\\\\xEF][\\\\x80-\\\\xBF]{2}|" + //triple-byte sequences   1110xxxx 10xxxxxx * 2
                    "[\\\\xF0-\\\\xF7][\\\\x80-\\\\xBF]{3}" //quadruple-byte sequence 11110xxx 10xxxxxx * 3
                    + ").*"
            , values[i]));
}

Если у вас есть массив байтов, который вы могли бы фильтровать еще более правильно:

BufferedReader bufferedReader = null;
try {
    bufferedReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes), "UTF-8"));
    for (String currentLine; (currentLine = bufferedReader.readLine()) != null;) {
        currentLine = currentLine.replaceAll(
                        "[\\x00-\\x7F]|" + //single-byte sequences   0xxxxxxx
                        "[\\xC0-\\xDF][\\x80-\\xBF]|" + //double-byte sequences   110xxxxx 10xxxxxx
                        "[\\xE0-\\xEF][\\x80-\\xBF]{2}|" + //triple-byte sequences   1110xxxx 10xxxxxx * 2
                        "[\\xF0-\\xF7][\\x80-\\xBF]{3}" //quadruple-byte sequence 11110xxx 10xxxxxx * 3
                , ""));
    }

Для того, чтобы сделать все веб-приложение совместимым с UTF8, читайте здесь:
Как получить UTF-8, работающий в Java Webapps
Подробнее о байтовых кодировках и строках.
Вы можете проверить свой шаблон здесь.
То же самое в PHP здесь.