Как удалить суррогатных символов в Java?

Я столкнулся с ситуацией, когда я получаю суррогатные символы в тексте, которые я сохраняю на MySql 5.1. Поскольку UTF-16 не поддерживается в этом, я хочу удалить эти суррогатные пары вручную с помощью java-метода, прежде чем сохранять его в базе данных.

На данный момент я написал следующий метод, и мне любопытно узнать, существует ли прямой и оптимальный способ справиться с этим.

Заранее благодарим за помощь.

public static String removeSurrogates(String query) {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < query.length() - 1; i++) {
        char firstChar = query.charAt(i);
        char nextChar = query.charAt(i+1);
        if (Character.isSurrogatePair(firstChar, nextChar) == false) {
            sb.append(firstChar);
        } else {
            i++;
        }
    }
    if (Character.isHighSurrogate(query.charAt(query.length() - 1)) == false
            && Character.isLowSurrogate(query.charAt(query.length() - 1)) == false) {
        sb.append(query.charAt(query.length() - 1));
    }

    return sb.toString();
}

Ответ 1

Вот пара вещей:

  • Character.isSurrogate(char c):

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

  • Проверка на пары кажется бессмысленной, почему бы просто не удалить всех суррогатов?

  • x == false эквивалентно !x

  • StringBuilder лучше в тех случаях, когда вам не нужна синхронизация (например, переменная, которая никогда не выходит из локальной области).

Я предлагаю следующее:

public static String removeSurrogates(String query) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < query.length(); i++) {
        char c = query.charAt(i);
        // !isSurrogate(c) in Java 7
        if (!(Character.isHighSurrogate(c) || Character.isLowSurrogate(c))) {
            sb.append(firstChar);
        }
    }
    return sb.toString();
}

Нарушение инструкции if

Вы спросили об этом утверждении:

if (!(Character.isHighSurrogate(c) || Character.isLowSurrogate(c))) {
    sb.append(firstChar);
}

Один из способов понять это - перерыв каждой операции в ее собственную функцию, поэтому вы можете видеть, что комбинация делает то, что вы ожидаете:

static boolean isSurrogate(char c) {
    return Character.isHighSurrogate(c) || Character.isLowSurrogate(c);
}

static boolean isNotSurrogate(char c) {
    return !isSurrogate(c);
}

...

if (isNotSurrogate(c)) {
    sb.append(firstChar);
}

Ответ 2

Строки Java хранятся в виде последовательностей 16-разрядных символов, но они представляют собой последовательности символов юникода. В юникодной терминологии они хранятся как единицы кода, а также кодовые точки модели. Таким образом, несколько бессмысленно говорить об удалении суррогатов, которых нет в представлении символьной/кодовой точки (если только у вас нет одиноких суррогатов, и в этом случае у вас есть другие проблемы).

Скорее всего, вы хотите удалить любые символы, которые потребуют суррогатов при кодировании. Это означает любой символ, который лежит за пределами базовой многоязычной плоскости. Вы можете сделать это с помощью простого регулярного выражения:

return query.replaceAll("[^\u0000-\uffff]", "");

Ответ 3

почему бы не просто

for (int i = 0; i < query.length(); i++) 
    char c = query.charAt(i);
    if(!isHighSurrogate(c) && !isLowSurrogate(c))
        sb.append(c);

вы, вероятно, должны заменить их на "?", а не на правильное стирание.

Ответ 4

Просто любопытно. Если char является высоким суррогатом, есть ли необходимость проверить следующий? Предполагается, что он является низким суррогатом. Модифицированная версия:

public static String removeSurrogates(String query) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < query.length(); i++) {
        char ch = query.charAt(i);
        if (Character.isHighSurrogate(ch))
            i++;//skip the next char is it supposed to be low surrogate
        else
            sb.append(ch);
    }    
    return sb.toString();
}

Ответ 5

если удалить, все эти решения полезны но если репалляция, ниже лучше

StringBuffer sb = new StringBuffer();
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if(Character.isHighSurrogate(c)){
            sb.append('*');
        }else if(!Character.isLowSurrogate(c)){
            sb.append(c);
        }
    }
    return sb.toString();